roy > naoya > 基礎プログラミングI·情報検索 > (6)配列
これまで2回にわたりアルバイト代を計算するプログラムを作成してきた。while-endを用いてユーザが入力したアルバイトの時間を加算して最終的に合計のバイト時間とバイト代を計算するというものであった。しかし、これまでに作成をしてきたプログラムにはまだ問題がある。
これまでのプログラムでは、gets.chomp!.to_fを用いてユーザの入力したアルバイト時間を受け取り変数(例えばtime)に代入したが、繰り返しを行う中で毎回入力した値が上書きで代入される。例えば、最初に5と入力し、2回目に3.5と入力した場合、timeには3.5が上書きで代入され、最初に代入されていて5は消えてしまう。したがってこのプログラムで最終的に得られるのは合計のバイト時間や平均のバイト時間のみであり、毎日の労働時間はわからない。つまり給与明細を発行することができない。
では、どうすれば毎日の勤務時間を全て覚えておくことができるだろうか。
1つ目の方法は、全て違う変数に代入するということである。例えば、time1、time2、time3、time4、....、time31としておくことができる。最初に入力した値はtime1に代入し、2番目に入力した値はtime2に代入、ということを延々と繰り返せば毎日の労働時間を覚えておくことができる。
しかし、勤務日数は既知ではないし、同じようなことを何度も書かなければならないのは煩わしい。このような問題を解決するために利用するのが配列である。
変数には通常1つの値しか代入しておくことができない。以下のケースでは、x = 10とすることで、以前xに代入されていた5は忘れてしまい、xの値は10となる。このように新しい値を代入すると、前に代入されていた値を忘れてしまうというのが通常の変数の特性であった。
x = 5 p x #=>5 x = 10 p x #=>10
これに対して、通常の変数を拡張して、複数の値を入れられるようにしたものが配列である。
x = 150:これまでの変数(1つの値のみ保存可能)
y = [150, 200, 380, 160, 240, 400]:配列(複数の値を保存可能)
上記ではyという配列変数には6個の値が代入されている。最大6個まで保存できるということではなく、いくつでも値を保存できる。配列変数は複数の値を,(カンマ)で区切って保存し、かつ全ての値を[]内に格納している。配列の中に代入されている値の呼び方について、単にyに代入されている値とすると、6個のうちのどれを指しているのかがわからなくなる。このため、配列変数を使用する場合は、配列内の何番目の値を表示するのか、または配列内の何番目に値を代入するのかを示す必要がある。
1番目からではなく0番目から開始していることに注意しよう。
次に0番目のデータや1番目のデータをRubyの記法にしたがって記述する方法を見てみよう。
配列の中の何番目の値であるかは、変数名[n]で表現することができる。一番最初の値であれば変数名[0]、2番目は変数名[1]となる。[]の中の数字のことを添字もしくはインデックスと呼ぶ。
配列について
プログラムの中で配列を使用する場合、まずはその変数が配列変数であることを示す(定義する)必要がある。
その変数が配列であることを定義する方法
1と2のうちどちらの方法でも良い。
まず、その変数が配列であることを定義する。代入をする場合は、配列の何番目に代入をするかインデックスで指定をする。一つの例を見てみよう(array.rb)。
#!/usr/koeki/bin/ruby item = [] #=>itemは配列 sum = 0 print "1つ目の商品の金額を入力してください\n" item[0] = gets.chomp!.to_i #=>itemの0番目に代入 p item #=>pは変数内の値を表示するメソッド sum += item[0] print "2つ目の商品の金額を入力してください\n" item[1] = gets.chomp!.to_i p item sum += item[1] print "3つ目の商品の金額を入力してください\n" item[2] = gets.chomp!.to_i p item sum += item[2] printf ("購入した商品は、%d円、%d円、%d円でした\n", item[0],item[1],item[2]) printf ("合計は%d円です\n",sum)
実行すると次の結果が得られる。
irsv{naoya}% ruby array.rb[Return] 1つ目の商品の金額を入力してください 100[Return] [100] 2つ目の商品の金額を入力してください 200[Return] [100,200] 3つ目の商品の金額を入力してください 300[Return] [100,200,300] 購入した商品は、100円、200円、300円でした 合計は600円です
このプログラムでは3つの商品の金額を入力し、最後に個々の商品金額と合計金額を表示する。個々の商品の金額はgetsメソッドを使ってキーボードからの入力を取得する際、item[0]やitem[1]に代入している。1つ目は配列変数itemの0番目、2つ目は1番目、3つ目は2番目というようにインデックスの値を1ずつ増やしながら代入しているため金額は上書きされず、全てを保持している。
実行結果内の、[100]や[100,200]はp itemに対する出力結果である。pメソッドは変数内の値を表示するが、指定した変数が配列の場合にはこのように[]をつけて代入されている値を全て表示する。これを見れば入力した値が順次代入されていることがわかる。
printfメソッドを使って配列内の値を表示する際は、printf ("購入した商品は、%d円、%d円、%d円でした",item[0],item[1],item[2])というように、インデックスに0、1、2を指定することで順番に取り出しができている。
取り扱う変数が配列である場合は、冒頭で配列であることを示す必要があることは述べた。2種類の方法(x=[]もしくはx=Array.new;xは任意の変数名)があったが、これらはいずれも初期値がない、からっぽの配列である。配列には最初から値を入れておくこともできる。
配列には数字だけでなく文字列も代入できる(通常の変数でも文字列を代入可能)。1つの配列変数の中に文字列と数字を混在させることもできる。
name = ["伊藤", "木村", "山田", "鈴木", "川島"] mixed = ["Hello", 34, "Windows", "Green", 125]
先ほどのプログラム(array.rb)でインデックスの指定を間違えた場合にはデータが上書きされる。3つの商品の金額を入力する際、item[0]、item[1]、item[2]としていたが、全てitem[0]とした場合、1番目、2番目に入力した値はそれぞれ上書きされ、item[0]内には最後に入力した値が保持される。
配列の使い方
array.rbのプログラムでは3つの商品の金額を入力し、最後に個々の商品の金額と合計を表示するが、5つの商品の金額を入力できるように変更してみよう。余裕がある人は10個まで対応できるように変更してみよう。プログラムを作成、実行した上で気がついた点や感想があればそれらも含めてメールで送ること。
制限時間は10分。完成しない場合は、途中まででも構わないので実行し、結果をメールで送ること。出席点は2点。提出要領は下記の通り。
Tips:emacsでの日本語入力のオンオフはCtrl-oです
Tips:Mewによるメールの送り方はMewコマンドを参照
配列を用いることで、最後に個々の商品の金額を表示することが可能となる。ただし配列に値を代入するにはインデックスを指定しなければならないため、プログラム内に同じようなことを何度も繰り返して入力しなければならない。
array.rbではitem[0]、item[1]、item[2]というようにインデックスに具体的な値を指定していたが、具体的な値を指定するかわりに、インデックスを変数にすると、このわずらわしさから解放される。以下の例を見てみよう(index.rb)。
#!/usr/koeki/bin/ruby
item = [] #=>itemは配列
number = 0
while true
print "何か数字を入力してね(終了はend)\n"
item[number] = gets.chomp!
if item[number] == "end"
then break
end
number += 1
end
p item
同じことを何度も繰り返して書くのはわずらわしいので、while-endの構文を用いている。ここではwhile trueを用い、キーボードから入力した値がendの場合に繰り返しから抜けるように指示をしている。
ここで注目すべきは、インデックスとしてnumberという変数を用いている点である。冒頭でnumber = 0として初期値に0を代入しているため、1回目のitem[number]はitem[0]と同義になる。繰り返しを行う中でnumber += 1として1ずつ増加しているため、2回目以降の繰り返しではitem[1]、item[2]、item[3]と変化する。繰り返しを継続するたびにnumberの値が1増加するので、キーボードから入力された値はインデックスを1ずつ増加させながら配列内に順次代入されることになる。
これを実行すると以下の通りとなる。
irsv{naoya}% ruby index.rb[Return] 何か数字を入力してね(終了はend) 100[Return] 何か数字を入力してね(終了はend) 200[Return] 何か数字を入力してね(終了はend) 300[Return] 何か数字を入力してね(終了はend) end[Return] ["100","200","300","end"]
参考までに、このプログラムではどこにもto_iが出現していない。このため配列内に代入された値を見ると"100"というように""が付いている。""がついているのは文字列であり、このままでは全てを足して合計を算出するということはできない。
また、配列の最後に"end"が値として代入されているという点も覚えておく必要がある。
配列に値を代入する際にインデックスに変数を指定したように、配列に代入されている値を表示する際にもインデックスを指定することができる。
例えば、item = [100,200,300]というように3つの商品の金額が代入されていたとする。ここで、3つの商品の金額を表示する場合、array.rbの書き方をそのままもってくれば
printf ("1個目%d円、2個目%d円、3個目%d円\n",item[0],item[1],item[2])
となる。既に気づいているだろうが、これもこのままでは使い勝手が悪い。商品を何個購入するかは既知ではないためである。ここでも配列のインデックスに変数を使用して、順番に値を読み出してみよう(index2.rb)。
#!/usr/koeki/bin/ruby item = [100,200,300] x = 0 sum = 0 while x < item.length printf ("%d個目 %d円\n",x+1,item[x]) sum += item[x] x += 1 end printf ("合計は%d円です\n",sum)
これを実行すると以下の通りとなる。
irsv{naoya}% ruby index2.rb[Return] 1個目 100円 2個目 200円 3個目 300円 合計は600円です
lengthメソッドについて
このプログラムではインデックスにxという変数を使用し、xの初期値を0とし、while文の中でxの値を1ずつ増加させながらitem内の値を1つずつ順番に読み出している。ここでwhile文を継続する条件であるx < item.lengthについて見てみよう。lengthは上述の通り、配列内の値の数を返してくれるメソッドである。itemという配列には現在3つの値が代入されているため、item.lengthは3であり、これはx < 3と書いてあるのと等価である。xの初期値は0でありwhile文の中で1ずつ増加させているため、1回目はitem[0]、2回目はitem[1]、3回目はitem[2]が表示される。4回目になった時点でxは3になっており、x < 3という条件を満たさなくなるので、これで繰り返しが終了となり、最後にendの下にあるprintfの行が実行され、合計金額が表示される。
前回作成したkadai3.rbをさらに改良し、以下の要件を満たすkadai4.rbを作成しなさい。
+------------------------------------------------+ | 2007年5月分給与明細票 | |氏名:公益太郎 | |勤務時間 | | 1日 3.5h 11日 4.5h 21日 2.5h 31日 5.0h | | 2日 6.0h 12日 0.0h 22日 0.0h | | 3日 2.5h 13日 8.0h 23日 0.0h 時給 700円 | | 4日 0.0h 14日 7.0h 24日 0.0h 合計時間 80.5h | | 5日 6.0h 15日 0.0h 25日 8.0h 金額 56350円 | | 6日 8.0h 16日 2.5h 26日 6.0h | | 7日 3.0h 17日 3.5h 27日 7.0h 平均4.2時間 | | 8日 0.0h 18日 3.0h 28日 0.0h 働きました | | 9日 0.0h 19日 5.5h 29日 0.0h ありがとう | |10日 4.0h 20日 6.0h 30日 0.0h ございます | | | +------------------------------------------------+
上記のように、1日から10日の右横に11日から20日の結果を並べて書くのはものすごく難しいので無理に挑戦しなくて良い(うまくできた場合は高得点が期待できるが)。また、実行する際にいちいち31回もデータを入力するのは面倒なので、5月第1週明細票などとして、7個だけデータを入力すると1週間分の明細表が表示されるようにしても良い。これを最低目標とし、8点満点で採点する。
今回のプログラムの改良を行うにあたっては、index.rbとindex2.rbが参考になる。最後に毎日の労働時間を表示するためには、あとで読み出せるように配列に代入しておく必要がある。この際、インデックスには数字を入れるのではなく変数を用いたほうが良い。index.rbではto_iがついていないため、入力された値が文字列として取り扱われていた。また、配列内の最後の値が"end"となっていた。このままでは合計が求められないので、このあたりの修正をいかに上手く行うかがポイントとなる。
明細票を作成する際の横棒や縦棒については、以下を参考にすると良い。
Tips:emacsでの日本語入力のオンオフはCtrl-oです
Tips:ktermでのプログラムの実行結果をメールに貼り付けるには、コピーしたい箇所をマウスで選択し、emacs(Mew)上でマウスの真ん中ボタンをクリックする
Tips:Mewによるメールの送り方はMewコマンドを参照