プログラミング+ Ruby超入門 (後編)

プログラミング+ Ruby超入門 (後編)

  • 沿って huawei
  • 17/11/2022

こんにちは。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)

画面には2100が表示されるはずです。

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に代入して結果を出力してみましょう。 こんなプログラムになります。

プログラミング+ Ruby超入門 (後編)

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が出力されましたね。プレイヤーの推察は間違っていたようです。

さきほどのプログラムでは、単にanswerguessの値をそれぞれ出力しただけでした。 もっとゲームっぽく、プレイヤーが入力した数字の値が正解に一致したときは「あたり!」、一致しないときは「はずれ!」などのメッセージを出してあげたいものです。 変数の値に応じてやることを変えるには、どうすればいいでしょうか。

そこで使うのが分岐です。Rubyではif文という分岐命令が使えます。if文は、ある条件が成り立つときと成り立たないときでやることが変わります。 さきほどのゲームのプログラムでif文を使い、プレイヤーの推察した数値が正解かどうかに応じて「あたり!」か「はずれ!」を出力するようにしてみましょう。 変数guessanswerが等しい(「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文ではifelseendをセットにして書けます。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文は、whileendをセットで書きます。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を入力したとしましょう。今度は変数guess55が代入された状態で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

このプログラムでは、まず変数i1を代入しておき、その状態でwhile文がきます。 このwhile文ではiが1000以下(この条件はRubyではi <= 1000と書きます)だったら中身が実行されます。

while文の中身はi = i + 1です。 これは前述の「変数を上書きする」で見たfoo = foo + 1と同じパターンで、変数の値に1を足し、その結果を同じ変数に代入し直します。 結果としてiは2になります。

この状態で再びwhile文の条件判定に戻ります。21000以下なので今回も条件を満たし、中身が実行されます。 同じように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は、変数answer0を代入しているだけです。iには先ほどと同じく、まず1が代入されます。while文の条件も先ほどと同じなので、まずはi <= 1000が成り立って中身が実行されます。

中身の最初はanswer = answer + iという文です。 この時点でanswerには0が、iには1が入っているので、answer + i0 + 1になり、その結果である1answerに代入されます。

続いてi = i + 1が実行され、再びwhile文の条件判定に戻り、やはり条件を満たすのでanswer = answer + iが再度実行されます。 いまはanswerには1が、iには2が入っているので、answer + i1 + 23になります。

同様にi1増やし、while文を一周してきて、次のanswer = answer + iでは、3 + 3answer6になります。これを繰り返していきます。

ここで、変数answerに足されていく値iに注目すると、最初は1、次は2、その次は3、……というふうになっています。 つまり、answerには、1 + 2 + 3 + ...の計算結果が入れられていくということです。 したがって、i1000になるまで繰り返して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にある多くのシミュレータの使い方を学ぶ。


株式会社角川アスキー総合研究所 お問い合わせフォーム

プログラミング+編集部では、法人向けセミナーや企業内研修の企画・実施、商品や出版物のレビュー記事作成、イベント取材のご依頼など、プログラミングや教育にまつわる幅広いご要望・ご相談を承っております。個人・法人・団体を問わず、ご興味のある方は上記フォームよりお気軽にお問い合わせください。


© KADOKAWA ASCII Research Laboratories, Inc. 2022

表示形式: PC ⁄ スマホ