目次

7.  配列とイテレータ

 

好きな数だけ単語の入力をしてもらい(1行に1単語、最後はEnterだけの空行)、 アルファベット順に並べ変えて出力するようなプログラムを書いてみましょう?いいですか?

さて、最初に・・—えーと、あー、ふむふむ。 何を使えばいいのかというと— あーと、んーと。

まだ出来ないようです。まずは、いくつあるかわからない数の単語を保存する方法と、 その全部をどうやってまとめるのかを知らないとだめですね。 これが分かれば他の変数とはごっちゃにならないですみます。 何か一覧表(list)のようなものに単語を入れないといけないようです。 つまり配列 が必要です。

配列とはコンピュータの中の一覧リストようなものです。 リストの中のすべての項目は変数のように振舞います。 特定の項目が何のオブジェクトを指し示しているかを知ることが出来るし、 それを別のオブジェクトにポイントしなおすことも出来ます。 では、配列について見てみることにしましょう。

[]
[5]
['Hello', 'Goodbye']

flavor = 'バニラ'              #  もちろんこれは配列ではありません。
[89.9, flavor, [true, false]]  #  ...でもこれは配列です。

最初は空の配列です。次はひとつの数字を含む配列で、 その次は2つの文字列を保持している配列です。 その次の行は普通の代入文です。そしてその次は3つのオブジェクトを保持している 配列を示しています。その中の最後の要素であるオブジェクト [truefalse]もまた、配列です。 変数がオブジェクトではないことを思い出してください。ここで、 最後の配列は順に、浮動小数点数、文字列 、そして、配列を 指し示しているわけです。 たとえ、あとからflavorを他のものを指し示すようにセット しなおしたとしても、もとの配列に変更が加わることはありません。

配列の中の特定のオブジェクトを見つけるために、それぞれの項目には インデックスとなる数が振ってあります。プログラマたちは(それと、 偶然にも多くの数学者たちも)ゼロから数える習慣があります。たとえ、その結果、 1番目の項目が項目0と呼ぶことになったとしてもです。 では、この番号を使って配列のオブジェクトをどう呼び出すかを見てみましょう。

names = ['Ada', 'Belle', 'Chris']

puts names
puts names[0]
puts names[1]
puts names[2]
puts names[3]  #  これは範囲外です。
Ada
Belle
Chris
Ada
Belle
Chris
nil

最初、puts namesによって、names配列に入っている それぞれの名前が表示されるのがわかります。その後で、puts names[0] を使って「1番目の」名前がプリントされます。その次は、puts names[1] で、「2番目」...以下同様です。はい、確かに紛らわしいのは私も認めます。 しかし、これには慣れるしか ないようです。とにかく物を数える時には 頭の中で 0から数えるようにして、「1番目」とか「2番目」とか言う言葉は 使わないようにしたほうが良いかもしれません。 たとえば、外食して5コースの料理を食べるとき、1番目のコース料理といわずに コース0と言いましょう。(そして、頭の中ではcourse[0]と思うわけです。) あなたの右手には5本の指があると思います。その番号は、0, 1, 2, 3, そして4です。 妻と私はジャグラーなんですが、2人で6本のクラブをジャグリングしているとき、 それらはクラブ0-5です。数ヵ月後にはクラブ6をジャグリングできるようにしたいと 思っています。(それができたときは、2人の間には7本のクラブがあることになります。) そのうち、「その配列の4番目の要素」といって間違えることなくmyArray[3]の ことを指すようになっていると思います。そのころには、「0番目」という単語を 使うようになってるかもしれません。実際にこの言葉は使われているんですよ。 プログラマか数学者の誰かに聞いてごらんなさい。

プログラムでは最後に、puts names[3]を試しています。 何が起こるのでしょう。エラーが起こるのを期待しますか? コンピュータに質問をしたとき、(コンピュータにとって) その質問が理解できない時には、エラーになります。 しかし、質問には意味があって、その答えが何もない という時もあります。項目3には何があるのでしょう? 何もありません。 names[3]はなんでしょう。nilです。 nilとは、Rubyの言い方で言う「なんにもなし」です。 nilは基本的には「他のどのオブジェクトでもない」という意味の特別の オブジェクトなのです。

この配列の項目に関する奇妙な番号付けの方法をマスターしたなら、 もう恐れることはありません。 さらに、いろいろな配列のメソッドを使えば、この奇妙な番号付けにさえでくわさないで すんでしまいます。こんな具合。

メソッドeach

eachを使うと、その配列が指し示しているそれぞれすべて(each)の オブジェクトに対して何か(なんでも好きなこと)をすることができます。 たとえば下のように、配列にいくつかの言語が入っているとして、そのそれぞれに対して なにか素敵なことを言いたい時にはこんなふうにします。

languages = ['English', '日本語', 'Ruby']

languages.each do |lang|
  puts '私は ' + lang + ' が好きだー!'
  puts 'あなたは?'
end

puts 'では、C++についても聞かせてください!'
puts '...'
私は English が好きだー!
あなたは?
私は 日本語 が好きだー!
あなたは?
私は Ruby が好きだー!
あなたは?
では、C++についても聞かせてください!
...

さて、何が起こったのでしょう。このやり方を使えば、配列の中の全部の要素を まったく番号を使わずに扱うことができます。これはナイスです。 上のプログラムを普通の言葉に翻訳するなら、「配列languagesの 中にあるオブジェクトそれぞれ(each)に対し、langという変数に 代入したうえで、endまでの間に書いてあるすべてのことを 実行(do)しなさい。」ということになります。 (知ってのとおり、C++というのはプログラミング言語の1つです。 この言語はRubyに比べると非常に覚えるのが難しくて、たいていの場合、C++のプログラムは 同じことをするRubyのプログラムの何倍もの長さになります。 (訳註:でも実行のスピードはずっと速いですが。))

あなたはこれを見て、「これはなんとなく前に習ったループに似てるなぁ」と、 思ったかもしれません。はい、確かに似ています。 重要な違いはeachがメソッドである、ということです。 whileendは、(そして、 do, if, elseや、その他 青色 で書いてあるすべての命令語も)メソッドではありません。 これらはRuby言語の基本的な文法の一部であって、たとえば =とか、括弧とか、普通の言語で言ったら句読点のようなものと 同じです。

でも、eachは違います。eachは配列のメソッドのひとつなのです。 eachのようにループと「同じように振舞う」メソッド はしばしばイテレータ という名前で呼ばれます。

イテレータに関して気をつけておくべきことは、必ず do...endという形が後に続くということです。 whileifのそばにはdoは用いられません。 doは主にイテレータとともに使われます。 (訳註:whileの後にdoを用いることもできます。 この場合のdoは省略されることが多いので、 doがイテレータと共に使われるというのはたいていの場合あっています。)

さて、もうひとつのかわいいイテレータを紹介しましょう。ただし、これは 配列のメソッドではなく、整数のメソッドです。

3.times do
  puts 'ヒップ ヒップ フレーー'  # ばんざーい
end
ヒップ ヒップ フレーー
ヒップ ヒップ フレーー
ヒップ ヒップ フレーー

練習問題

• では、この章の最初で述べたプログラムを書いてみましょう。
ヒント: 配列を順番に並び替える(ソートする)には 素敵なメソッド sortがあります。 これを使いましょう。
(訳注:配列 ary の最後に要素 elem を追加するには、ary << elem と 記述します。)

• 上のプログラムをsortメソッドなしで 書けますか。プログラミングの大部分は、問題解決にあります。 これはいい練習になります。

もっと配列メソッド

ここまでで、eachsortを覚えました。 しかし、配列にはその他にたくさんのメソッドが存在します。その数は文字列のメソッドと 同じくらいです。実際、そのいくつか(length, reverse, +, そして*など)は文字列と同じような動作をします。ただし、 文字列で文字に当たるものが配列では項目になりますが。 その他にも、配列特有のメソッドとしてlastjoinなどもあります。 さらに、pushpopのようなメソッドはそれを呼んだ配列のほうを 変化させます。 そして、文字列のメソッドのときと同じように、それについてどこを探せばいいかを 知っている限り(つまりこれから説明していく場所ですが)、すべてを覚える必要は 全くありません。

さて、では最初にto_sjoinを見ていきましょう。 jointo_sとほとんど同じように働きます。 違いは、joinのほうが配列のそれぞれのオブジェクトの間に 文字列を挿入することです。 見てみましょう。

foods = ['アーティチョーク', 'ブリオッシュ', 'キャラメル']
puts foods
puts
puts foods.to_s
puts
puts foods.join(', ')
puts
puts foods.join('  :)  ') + '  8)'

200.times do
  puts []
end
アーティチョーク
ブリオッシュ
キャラメル

アーティチョークブリオッシュキャラメル

アーティチョーク, ブリオッシュ, キャラメル

アーティチョーク  :)  ブリオッシュ  :)  キャラメル  8)

見てのとおり、putsは配列を他のオブジェクトとは違ったふうに 扱っています。つまり、(訳註:ただ変換したりするだけではなくて)配列の中にある オブジェクトそれぞれに対して別々にputsを呼び出しています。 これが、最後の例で、空の配列に対してputsを200回呼び出していても 何も出力さてていない理由です。空の配列には何のオブジェクトも指し示されていない ので、putsに対しては何もすることがありません。 (そして、何もしないのを200回繰り返してもやっぱり何もしません。) では、中に別の配列が入っている配列をputsして見ましょう。 期待通り実行されますか。

いままで空の行をputsしようとして、空文字列を 残していたのに気づいていましたか。putsputs ''と 同じことをします。

さて今度は、push, popそれからlastというメソッドについて 見てみましょう。pushpopは、+-のように、 互いに反対の意味を持つペアのメソッドです。 pushは配列の最後尾にオブジェクトを追加します。反対に、popは 最後尾の要素となるオブジェクトを取り除きます。(そしてそれがなんだったかを返します。) lastpopに似ていて、最後尾の要素がなにかを告げますが、 その要素を取り除くことはしません。 繰り返しますが、pushpop配列そのものを変化させてしまいます。

favorites = []
favorites.push 'バラの上の雨粒'
favorites.push '子猫の上のウィスキー'

puts favorites[0]
puts favorites.last
puts favorites.length

puts favorites.pop
puts favorites
puts favorites.length
バラの上の雨粒
子猫の上のウィスキー
2
子猫の上のウィスキー
バラの上の雨粒
1

練習問題

• 以前、メソッドの章で書いた 目次を表示するプログラムを修正してみましょう。その際、プログラムの最初で 目次の情報(つまり、章の名前やページ番号など)をすべてひとつの配列にしまいます。 その後、その配列から情報を取り出して美しくフォーマットされた目次を出力します。

ここまでたくさんの数のメソッドを習ってきました。 さて次は、いよいよ自分でメソッドを作る方法を覚えます。

 

目次