こんにちは。Rubyを作りながらRubyを学ぼうという連載企画、第2回です。 第1回ではRubyインタプリタとは何かを学び、Rubyでプログラミングするための環境の準備をして、簡単な計算をする初めてのRubyプログラムを書いてみました。
今回は、Rubyを作るために最小限必要なRubyの知識として、「変数」と「分岐」、そして「ループ」という概念を学びます。
前回の記事で最後に書いたのは、簡単な計算をして出力する次のようなプログラムでした。
p((1 + 2) / 3 * 4 * (56 / 7 + 8 + 9))
このプログラムをRubyインタプリタで実行すると、計算結果である「100
」がすぐに出力されます。 Windowsで実行した例はこんな具合になるはずです(C:¥Ruby
フォルダのcalc0.rb
というファイルにこのプログラムを保存した場合の例です)。
C:¥Ruby > ruby calc0.rb ⏎100C:¥Ruby >
念のため補足しますが、上記の内容を適当な名前をつけたファイルに保存し、そのファイルの名前をターミナルやコマンドラインでruby
コマンドの後ろに指定して、エンターキーを押す、というのが、「このプログラムをRubyインタプリタで実行する」ということの意味です。これについては第一回で詳しく説明したので、「具体的には何をすればいいんだ?」と思って手が止まってしまった方は第一回をもう一度読み直してみてください。
ここからは、実行結果について説明する場合をのぞき、ターミナルやコマンドラインでプログラムを実行する方法をいちいち説明しないことにします。なにか新しいプログラムが出てきたら、それを新しいファイルに書いて名前を付けて保存し、ruby
コマンドで実行してください。
式を計算するだけなら、すぐに結果が出力されるので、これでも問題ありません。 しかし実際のプログラムでは、計算結果をいったんわきに置いておいて後で出力したい、といったことが頻繁にあります。
そんなときに使えるのが変数です。変数とは、いうなれば「値を覚えてくれるもの」です。たとえば、上記の計算結果をすぐに出力するのではなく、変数に覚えさせておくプログラムは、次のように書けます。 このプログラムでは、まずイコール記号=
より右側に書いてある数式の計算をして、その結果を「answer
」という名前の変数に覚えさせています。
answer = (1 + 2) / 3 * 4 * (56 / 7 + 8 + 9)
このプログラムは変数に値を覚えさせるだけで、いっさい出力をさせていないため、実行しても何も表示されません。 実際に上記の1行だけを書いたRubyプログラムを保存して、それを実行してみてください。 (しつこいようですが、プログラムを実行するときは、新しいファイルに適当な名前でプログラムの内容を保存し、そのファイルをターミナルやコマンドラインでruby
コマンドに指定して、エンターキーを押す、ですよ。)
C:¥Ruby > ruby calc1.rb ⏎←何も表示されない!C:¥Ruby
answer
に覚えさせた計算結果を、実行時にターミナルやコマンドラインに表示されるようにするには、プログラムを次のように改良します。
answer = (1 + 2) / 3 * 4 * (56 / 7 + 8 + 9)p(answer)
このプログラムを実行してみましょう。今度は画面に100
が表示されましたね。
C:¥Ruby > ruby calc2.rb ⏎100C:¥Ruby >
覚えておいた値をすぐに出力するだけでは、変数のありがたみが感じられないかもしれません。 そこで、変数に値を覚えさせてから、その覚えさせた値を出力するまでの間に、何か別の計算をしてみましょう。 次のプログラムを見てください。(「#
」から行末まではRubyインタプリタによってプログラムの一部とみなされないので、補足説明を書くのに使っています。)
answer = (1 + 2) / 3 * 4 * (56 / 7 + 8 + 9)p(1 + 1)# 別の計算結果をその場で出力p(answer)
画面には2
と100
が表示されるはずです。
C:¥Ruby > ruby calc3.rb ⏎2100C:¥Ruby >
変数を使えるのはp
で出力するときだけではありません。変数を使った計算式を書くこともできます。 つまり、こんなふうに計算式に変数を書けば、前に変数に覚えさせておいた値が計算で使われます。
answer = (1 + 2) / 3 * 4 * (56 / 7 + 8 + 9)p(answer + 1) # 101 が出力される
また、変数は複数作ることができ、それらを組み合わせた計算もできます。
one = 1two = one + 1p(one + two * two) # 5 が出力される
変数に値を覚えさせることを「代入する」と言います。また、覚えさせた値を取り出すことを「参照する」または「値を読み出す」などと言います。
変数の名前に使える文字は、アルファベットの小文字とアンダースコア(_
)です。 ただし、Rubyが特別な意味をもたせている語(後述する「if」や「while」など)を変数の名前にすることはできません。 なお、出力命令の「p」と同名の変数は定義できます(混乱するのでしないほうがよいですが)。このことは別の回で少し触れます。
変数は2回以上読み出すこともできます。すなわち、変数から値を読み出しても、変数がその値を勝手に忘れることはありません。
foo = 1 # foo に 1 を代入するp(foo)# 1 が出力されるp(foo)# もう一回 1 が出力される
新たな値を覚えさせることで、古い値を忘れさせることはできます。
foo = 1 # foo に 1 を代入するp(foo)# 1 が出力されるfoo = 2 # foo に 2 を代入(上書き)するp(foo)# 2 が出力される
3行めのfoo = 2
の代入によって、変数foo
は以前覚えていた1
を忘れました。 代わりにfoo
はいま2
を覚えています。
少しややこしい例を見てみましょう。
foo = 1p(foo)# 1 が出力されるfoo = foo + 1p(foo)# 2 が出力される
3行めのfoo = foo + 1
では、まずイコール記号より右側のfoo + 1
が計算されます。この時点では変数foo
には1が入っているので、foo + 1
の計算結果は2になります。 そして、この2という値をfoo
に代入(上書き)しているわけです。つまり、このプログラムの3行めは、実質的にfoo = 2
と同じ意味になります。
「変数 = 式
」というのは、数学だったら「変数と式が等しい」という意味ですが、Rubyプログラムの中ではあくまで「変数に式の計算結果を覚えさせる(代入する)」という意味であることに注意しましょう。
ダメ押しです。次のプログラムで最後に5
が出力されることを確認してください。
foo = 1p(foo)# 1 が出力されるfoo = foo + 1p(foo)# 2 が出力されるfoo = foo + foo + 1p(foo)# 5 が出力される
さて、次は「分岐」です。ここまでに出てきた例では、ファイルに書かれた内容を1行ずつ順番に実行するプログラムだけを考えてきました。 しかし、たとえばゲームのプログラムを作りたいと思ったら、いろいろな条件によって実行される内容を変えるための方法が必要になります。
ためしに簡単な計算ゲームを考えてみましょう。プレイヤーが1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10
の答えを推察するゲームを作ることにします。 ゲームなので、プレイヤーに数字を入力してもらう方法が必要ですね。これにはRubyのgets.to_i
という命令が使えます。
値の出力に使っているp
に比べると、gets.to_i
はごちゃごちゃしていて命令っぽく見えないかもしれませんが、実は「入力を受け取る」という仕事と、受け取ったものを「数値に変える」という仕事を続けて行う命令で、そのため見た目が少しいかつくなっています。
まずはとっかかりとして、1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10
の計算結果とプレイヤーが入力した数の値を、それぞれ変数answer
と変数guess
に代入して結果を出力してみましょう。 こんなプログラムになります。
answer = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10guess = gets.to_ip(answer)p(guess)
このプログラムを実行してみてください(ファイルに保存してruby
コマンドで実行ですよ)。 いままでのプログラムの実行結果とは違い、特に何も出力されないままになると思います。 これは、プログラムがプレイヤーからの入力を待っている状態です。そこで、適当な数字(たとえば10
)をキーボードで入力し、エンターキーを押してください。
C:¥Ruby > ruby calc4.rb ⏎← 何も出力されない(入力を待っている状態)
↓
C:¥Ruby > ruby calc4.rb ⏎10 ⏎← 10と入れてエンターキーを押す
↓
C:¥Ruby > ruby calc4.rb ⏎10 ⏎55← 正解10← プレイヤーが入力した値
正解の55
と、入力した10
が出力されましたね。プレイヤーの推察は間違っていたようです。
さきほどのプログラムでは、単にanswer
とguess
の値をそれぞれ出力しただけでした。 もっとゲームっぽく、プレイヤーが入力した数字の値が正解に一致したときは「あたり!」、一致しないときは「はずれ!」などのメッセージを出してあげたいものです。 変数の値に応じてやることを変えるには、どうすればいいでしょうか。
そこで使うのが分岐です。Rubyではif
文という分岐命令が使えます。if
文は、ある条件が成り立つときと成り立たないときでやることが変わります。 さきほどのゲームのプログラムでif
文を使い、プレイヤーの推察した数値が正解かどうかに応じて「あたり!」か「はずれ!」を出力するようにしてみましょう。 変数guess
とanswer
が等しい(「guess == answer
」)ときはp("あたり!")
を、そうでないときはp("はずれ!")
を実行するようになります。
answer = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10guess = gets.to_iif guess == answer# 変数guessと変数answerは等しい? p("あたり!") # 等しい場合else p("はずれ!")# 等しくない場合end
このプログラムでは、「等しい」を表すのに=
を二つ並べた「==
」を使っていることにも注目してください。「=
」単体は代入のときに使うのでした(Rubyに限らず、わりと多くのプログラミング言語でも同じです)。ちなみに、「等しくない」を表すには「!=
」と書きます。
このプログラムを実行してみてください。プレイヤーとして55
を入力した場合には「"あたり!"
」、それ以外の数字を入力した場合には「"はずれ!"
」と表示されてプログラムが終了するはずです。
C:¥Ruby > ruby calc5.rb ⏎← 何も出力されない(入力を待っている状態)
↓
C:¥Ruby > ruby calc5.rb ⏎10 ⏎← 10と入れてエンターキーを押す
↓
C:¥Ruby > ruby calc5.rb ⏎10 ⏎"はずれ!"← 入力が間違っている場合の表示
プログラムの中では「"はずれ!"
」とか「"あたり!"
」のように文字の並びを「"
」(ダブルクォーテーション)で括っています。 これは、Rubyインタプリタに文字列として扱ってもらいたい値は「"
」で括るという約束になっているからです。
この約束は、いかにも文字列のように見える「あたり!
」などだけでなく、数字の場合も同じです。 つまり、裸の「1
」はRubyプログラムでは数(正確には整数値)として扱われますが、ダブルクォーテーションで括った「"1"
」は文字列として扱われます。 数ではないので、足し算や掛け算はできません。
逆に、Rubyプログラム中で「"あたり!"
」を裸の「あたり!
」としてしまうと、Rubyインタプリタに文字列だと思ってもらえません。 未定義の変数か何かの名前として扱われるのでエラーになります。
このプログラムのように、if
文ではif
とelse
とend
をセットにして書けます。Rubyインタプリタは、if
の直後に書かれた条件が成り立つときはelse
の前までにある命令(複数の命令が並んでいることもあります)を実行し、成り立たないときはelse
の後とからend
までの命令を実行します。
条件が成り立つときだけ何かしたい(つまり条件が成り立たない場合には何もしない)なら、else
とその後の命令を省略することもできます。
answer = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10guess = gets.to_iif guess == answer p("あたりです!") p("おめでとうございます!")end
if
文の中に別のif
文を入れることもできます。たとえば、はずれのとき「"はずれ!"
」と出力するだけではそっけないので、ヒントを出してあげましょう。
answer = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10guess = gets.to_iif guess == answer p("あたりです!") p("おめでとうございます!")else p("はずれ!") if guess > answer# 変数guessは変数answerよりも大きい? p("それはちょっと大きすぎるよ!") else p("それはちょっと小さすぎるよ!") endendp("また遊んでね!")
これではずれのとき、入力した値が大きすぎたか小さすぎたかを教えてくれるようになります。 また、プログラムの一番最後で「"また遊んでね!"
」というメッセージを出すようにしました。これはif
文のどちらを実行した場合でも出力されます。このように、if
文のどちらかが実行された後は、end
の直後に実行が移ります。
if
文の中身の行は空白で始めるという慣習があります。この空白をインデントと言います。インデントを綺麗にそろえることで、どこからどこまでがif
文の中身なのかを視覚的にわかりやすくできます。
分岐のついでに「ループ」にも触れておきます。
さきほどの数当てゲームのプログラムは、はずれたらメッセージを出して終わってしまいます。しかし、はずれたプレイヤーは当てるまで何度も挑戦したいでしょう。そっちのほうがゲームとしてもふつうです。 当たるまで繰り返しプレイヤーからの入力を待ちたいといった場合に使えるのが「ループ」です。 Rubyにはwhile
文というループ命令があります。while
文は、「ある条件が成り立つ限り、同じことを繰り返し実行する」という命令です。
いまの場合は、変数answerと変数guessの値が同じにならない場合には「はずれ!」と出力して再びプレイヤーの入力を待つようにしたいので、「値が同じでない」という条件を判定できる「!=
」を使って、こんなプログラムを書けます。(先ほどまでとは違うプログラムになるので、新しいまっさらなファイルに保存してから実行してくださいね。)
answer = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10guess = gets.to_iwhile answer != guess p("はずれ!") guess = gets.to_iendp("あたり!")
while
文は、while
とend
をセットで書きます。Rubyインタプリタは、while
の直後に書かれた条件が成り立つときはend
の前までの命令を実行し、そうでないときはend
の後ろに飛びます。条件が成り立つときに命令が実行されるという点ではif
文に似ていますが、異なるのは、中身を実行し終えたときにwhile
文の先頭に戻ってきて、条件が成り立つかどうかのチェックから繰り返されるという点です(if
文では対応するend
の後ろに進んでしまいます)。
たとえば、プレイヤーが50
を入力したとします。これははずれなので、while
文(プログラムでいうと3行め)の条件にあたるanswer != guess
が成り立ちます。よって、while
の中身のうち、まずプログラムの4行めにある「p("はずれ!")
」が実行されます。それからプログラムの5行めで、再び変数guess
に読み込むべくプレイヤーの入力を待ちます。これでwhile
文の中身の実行は終わりなので、while
文の最初に戻ります。
もしプレイヤーが今度は51
を入力したとすると、もう一度answer != guess
の判定を行い、51もまたはずれなので、while
文の中身が実行されます。先ほどと同じように4行め、5行めが実行され、三たびプレイヤーの入力を待つことになります。
今度はプレイヤーが55
を入力したとしましょう。今度は変数guess
に55
が代入された状態でwhile
文のanswer != guess
が判定されます。55はあたりなので、今度はwhile
文の条件が成り立ちません。よってend
の後ろ(プログラムの最終行)に進み、「あたり!」と出力して終わります。
以上で今回新しく覚える内容は終わりです。「これだけ?」と思うかもしれませんが、変数と分岐とループは今後ものすごく頻出するプログラミングの超基本アイテムなので、息をするように扱えるようになっておく必要があります。そこで、違う例題でもう一度練習してみましょう。
題材は、1
から1000
までの足し算です。ここまでのプログラムでは、1
から10
までの足し算をこんなふうに書いていました。
answer = 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10
これくらいなら大したことないですが、1
から1000
までとなると、同じように書き下すのは大変です。こういう処理こそコンピュータに計算させましょう。
とっかかりとして、1
から1000
まで代入を繰り返すwhile
文を書いてみます。
i = 1while i <= 1000 i = i + 1end
このプログラムでは、まず変数i
に1
を代入しておき、その状態でwhile
文がきます。 このwhile
文ではi
が1000以下(この条件はRubyではi <= 1000
と書きます)だったら中身が実行されます。
while
文の中身はi = i + 1
です。 これは前述の「変数を上書きする」で見たfoo = foo + 1
と同じパターンで、変数の値に1を足し、その結果を同じ変数に代入し直します。 結果としてi
は2になります。
この状態で再びwhile
文の条件判定に戻ります。2
は1000
以下なので今回も条件を満たし、中身が実行されます。 同じようにi = i + 1
を実行して、今度はi
が3になります。 つまり、while
文の中身が実行されるたびにi
の値が1ずつ増えていきます。
そしてi
が1001になったとき、初めてi <= 1000
という条件が成り立たなくなり、while
文が終了します。
こういうwhile
文は非常によくあるパターンで、繰り返し計算をしたい場合に出現します。ぜひパターンとして覚えてしまいましょう。
さて、いましたいのは1 + 2 + ... + 1000
という計算でした。先ほどのwhile
文では1ずつ増える数値が変数に上書きされていくだけで、足し算はされません。 1ずつ増える数値を足しこんでいくには、次のようにします。
answer = 0# 追加i = 1while i <= 1000 answer = answer + i# 追加 i = i + 1endp(answer)# 追加
先ほどのプログラムに3行追加しました。これでどうなるか考えてみましょう。
最初に追加したanswer = 0
は、変数answer
に0
を代入しているだけです。i
には先ほどと同じく、まず1
が代入されます。while
文の条件も先ほどと同じなので、まずはi <= 1000
が成り立って中身が実行されます。
中身の最初はanswer = answer + i
という文です。 この時点でanswer
には0
が、i
には1
が入っているので、answer + i
は0 + 1
になり、その結果である1
がanswer
に代入されます。
続いてi = i + 1
が実行され、再びwhile
文の条件判定に戻り、やはり条件を満たすのでanswer = answer + i
が再度実行されます。 いまはanswer
には1
が、i
には2
が入っているので、answer + i
は1 + 2
で3
になります。
同様にi
を1
増やし、while
文を一周してきて、次のanswer = answer + i
では、3 + 3
でanswer
が6
になります。これを繰り返していきます。
ここで、変数answer
に足されていく値i
に注目すると、最初は1
、次は2
、その次は3
、……というふうになっています。 つまり、answer
には、1 + 2 + 3 + ...
の計算結果が入れられていくということです。 したがって、i
が1000
になるまで繰り返してwhile
文が終わったら、その時点でanswer
には1
から1000
までを足した計算結果が入っていることになります。
その計算結果を最後にp(answer)
で出力して終了です。
すでにRubyを知っている人は、ここまで読んで、「Rubyにはもっと良い書き方があるのに、なぜこんなふうに書くのか」と思っていることでしょう (たとえば1.upto(1000) {|i| answer += i }
やanswer = (1..1000).inject(0, &:+)
のような)。 ここで説明しているMinRuby(最小のRuby)は、メソッド呼び出しのような「高級な」言語機能を持たないことにします(ただの関数はあります。次回説明します)。 よって、ここでもメソッド呼び出しは説明しません。
「そんなのRubyではない」という意見は正当です。 メソッド呼び出しはプログラミング言語にとって必要不可欠なものではありませんが、プログラミング言語は人間がコンピュータに指示を与えるためのものなので、人間が読みやすく書きやすいことは不可欠に近いくらい重要なことです。 しかし、この連載の目的はMinRubyのインタプリタを作ることなので、まずは説明する言語機能も必要最小限にしています。 少し煩雑ですが、しばらくお付き合いください。メソッド呼び出しのような高級な言語機能については、連載の後半で説明するかもしれません。
第2回では、「変数」というものと、if
文(分岐)とwhile
文(ループ)を説明しました。 第1回に比べて、ちょっとはプログラミングをしている感じが出てきたと思います。 今回の例題をいろいろいじったり、以下に用意した練習問題をやってみたりして、今回覚えた機能に慣れておいてください。
次回はいよいよ、インタプリタを作る準備として、「木構造」と呼ばれるデータの扱い方を説明します。 それを題材として、MinRubyの「データ構造」と「関数」も説明します。お楽しみに。
理解をさらに深めるための練習問題です。
プログラミングは、実際に手を動かして知識に血肉を通わせることが重要です。 実際にプログラムを書こうとすると、理解していたつもりでも案外時間がかかるもの。でも、それは普通のことです。ゆっくりでかまいませんので、じっくり考えてみましょう。これをすらすら解けるようになったら次回、というペースでも構いません。
次のプログラムは上で説明したプログラムとほとんど同じですが、計算結果が間違っています。このプログラムが何を計算しているのか、考えてみてください。
answer = 0i = 1while i <= 1000 i = i + 1 answer = answer + iendp(answer)
ヒント:answer = answer + i
の文の直前にp(i)
という命令を追加して、answer
に実際に足されている値を見てみるといいでしょう。 (1000は大きすぎるので、5くらいに置き換えて考えてみてください)
1から1000のうち、偶数だけを足し合わせた値(つまり 2 + 4 + 6 + ... + 998 + 1000)を計算するプログラムを書いてください。
ヒント: 現状のプログラムでは無条件にanswer = answer + i
としているところを、i
が偶数の場合にだけ実行するようにすればいいでしょう。特定の条件のときだけ実行するにはif文を使えばいいのでしたね。偶数かどうかを判定するのは、i
を2で割った余りが0であるかどうか、つまりi % 2 == 0
でできます。
FizzBuzzというプログラムがあります。1から100までの数を順番に出力しますが、3で割り切れる数の場合は数の代わりに"Fizz"を出力し、5で割り切れる数の場合は数の代わりに"Buzz"を出力し、3でも5でも割り切れる数の場合は"FizzBuzz"を出力する、というプログラムです。
FizzBuzzを書いてみてください。実行したときに次のような出力をすれば正解です。
12"Fizz"4"Buzz""Fizz"78"Fizz""Buzz11"Fizz"1314"FizzBuzz"...
ヒント1: まず、while
文を使って1から100まで繰り返すプログラムを書きましょう(開発中は100ではなく20くらいまでにしておくとデバッグしやすいかもしれません)。while
文の中でやる処理は、(1)その数字を出力する、(2)"Fizz"
を出力する、(3)"Buzz"
を出力する、(4)"FizzBuzz"
を出力する、のいずれかになります。うまく分岐を組んでください。FizzBuzzを口に出してやってみると、条件が頭の中で整理できると思います。
ヒント2: 分岐の組み方はいろいろありますが、たとえば次のような組み方が考えられます。
if i % 3 == 0 if i % 5 == 0 ... else ... endelse if i % 5 == 0 ... else ... endend
開いている4箇所に、(1)から(4)の処理をうまく入れてみてください。
ツイートする
カテゴリートップへ
■2019年7月6日 (土) 13:00~19:00
東京都千代田区五番町3-1
今年3月23日に行われたセミナー「量子コンピュータで学ぶ量子プログラミング入門」を好評につき、リピート開催。これまでの「紙と鉛筆で学ぶ量子コンピュータ入門」と異なり、今回のハンズオンは量子コンピュータで計算させるために必要な「量子プログラミング」の基礎を学ぶことが目標。IBMの協力を得て、基本的なゲートを組み合わせて量子回路を構成するプログラミング手法と、IBMのQiskitにある多くのシミュレータの使い方を学ぶ。
株式会社角川アスキー総合研究所 お問い合わせフォーム
プログラミング+編集部では、法人向けセミナーや企業内研修の企画・実施、商品や出版物のレビュー記事作成、イベント取材のご依頼など、プログラミングや教育にまつわる幅広いご要望・ご相談を承っております。個人・法人・団体を問わず、ご興味のある方は上記フォームよりお気軽にお問い合わせください。
表示形式: PC ⁄ スマホ