roy > naoya > 基礎プログラミングI > (5)配列

(5) 配列

[1] 配列への導入

前回のレポート課題は、while-endを用いてユーザが入力した毎回のレポートの得点を加算し、最終的に合計や平均を求めるというものであった。このプログラムをさらに発展させることを考えてみよう。

前回のプログラムでは、gets.chomp!.to_fを用いてユーザの入力した得点を受け取り変数(例えばreport)に代入したが、繰り返しを行う中で毎回入力した値が上書きで代入された。例えば、最初に7と入力し、2回目に6.5と入力した場合、reportには6.5が上書きで代入され、最初に代入されていた7は消えてしまう。したがってこのプログラムで最終的に得られるのは合計や平均値のみであり、毎回のレポート得点はわからない。

レポートは合計得点がわかれば良いかもしれないが、レジのプログラムを作る場合、最後にレシートを作成しなければならないため、入力した個々の値を全て覚えておけることが望ましい。では、どうすれば全ての値を覚えておくことができるだろか。

すぐに思いつくのは、全て違う変数に代入するという方法である。例えば、report1report2report3report4、....、report8としておくことができる。最初に入力した値はreport1に代入し、2番目に入力した値はreport2に代入、ということを延々と繰り返せばすべての得点を覚えておくことができる。

しかし、レジのプログラムでは商品の購入数が決まっていないため、あらかじめ準備しておく変数の数が定まらない。また、同じようなことを何度も書くのは効率的ではない。このような問題を解決するために利用するのが配列である。

[2] 配列とは

変数には通常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個のうちのどれを指しているのかがわからなくなる。このため、配列変数を使用する場合は、配列内の何番目の値を表示するのか、または配列内の何番目に値を代入するのかを示す必要がある。

  • 150:yの中の0番目の要素
  • 200:yの中の1番目の要素
  • 380:yの中の2番目の要素
  • 160:yの中の3番目の要素
  • 240:yの中の4番目の要素
  • 400:yの中の5番目の要素

1番目からではなく0番目から開始していることに注意しよう。

次に0番目の要素や1番目の要素をRubyの記法にしたがって記述する方法を見てみよう。

  • yの中の0番目の要素:y[0]
  • yの中の1番目の要素:y[1]
  • yの中の2番目の要素:y[2]
  • yの中の3番目の要素:y[3]
  • yの中の4番目の要素:y[4]
  • yの中の5番目の要素:y[5]

配列の中の何番目の値であるかは、変数名[n]で表現することができる。一番最初の値であれば変数名[0]、2番目は変数名[1]となる。[]の中の数字のことを添字もしくはインデックスと呼ぶ。

配列について

  • 複数の値を保持することができる変数を配列という。
  • 複数のデータは[]内に,(カンマ)でつなげて格納する。
  • hoge = [10, 20, 30]であれば、10は0番目、20は1番目、30は2番目のデータとなり、それぞれhoge[0]、hoge[1]、hoge[2]と表記する。

[3] 配列の基本的な使い方

プログラムの中で配列を使用する場合、まずはその変数が配列変数であることを示す必要がある。

その変数が配列であることを宣言する方法

  1. x = []
  2. x = Array.new

1と2のうちどちらの方法でも良い。

配列への値の代入と、代入された値の表示

まず、その変数が配列であることを宣言する。代入をする場合は、配列の何番目に代入をするかインデックスで指定をする。例を見てみよう(array.rb)。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

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)

実行すると次の結果が得られる。

sime{c11xxxx}% chmod +x array.rb[Return]
sime{c11xxxx}% ./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は任意の変数名)があったが、これらはいずれも初期値がない、からっぽの配列である。配列には最初から値を入れておくこともできる。

  • hoge = [100,200,300,400] とすれば最初から4つのデータ(hoge[0]からhoge[3]まで)が代入された配列が作られる。
  • hoge = Array.new(5,10) とすればhoge[0]からhoge[4]まで10が代入された配列が作られる。つまり、Array.new(長さ,初期値)であり、hoge = [10,10,10,10,10]と同義になる。

配列には文字も代入できる

配列には数字だけでなく文字列も代入できる(通常の変数でも文字列を代入可能)。1つの配列変数の中に文字列と数字を混在させることもできる。

name = ["伊藤", "木村", "山田", "鈴木", "川島"]
mixed = ["Hello", 34, "Windows", "Green", 125]

インデックスの指定を間違えると値が上書きされる

先ほどのプログラム(array.rb)でインデックスの指定を間違えた場合にはデータが上書きされる。3つの商品の金額を入力する際、item[0]item[1]item[2]としていたが、全てitem[0]とした場合、1番目、2番目に入力した値はそれぞれ上書きされ、item[0]内には最後に入力した値が保持される。

配列の使い方

  • 配列に値を代入したり、配列内の値を読み出す場合にはhoge[0]hoge[1]のように、何番目の値なのかを具体的に指定する。
  • hoge[x]xの部分をインデックスと呼ぶ。
  • インデックスは0や1などの具体的な値のかわりに変数で指定してもよい。
  • 配列には数字のみでなく文字列を代入することもできる

[4] インデックスを変数にして代入する

配列を用いることで、入力したすべての値を保持することができ、最後にすべて表示することが可能となる。ただし配列に値を代入するにはインデックスを指定しなければならないため、プログラム内に同じようなことを何度も繰り返して入力しなければならないという問題がある。

これでは、変数を必要な数だけ準備しているのと同じことである。しかし、インデックスに具体的な値を指定するかわりに、インデックスを変数にすると、このわずらわしさから解放される。以下の例を見てみよう(index1.rb)。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

item = []  #=>itemは配列
number = 0

while true
  print"何か数字を入力してね(終了はend)\n"
  item[number] = gets.chomp!
  if item[number] == "end"
     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ずつ増加させながら配列内に順次代入されることになる。

これを実行すると以下の通りとなる。

sime{c11xxxx}% chmod +x index1.rb[Return]
sime{c11xxxx}% ./index1.rb[Return]
何か数字を入力してね(終了はend)
100[Return]
何か数字を入力してね(終了はend)
200[Return]
何か数字を入力してね(終了はend)
300[Return]
何か数字を入力してね(終了はend)
end[Return]
["100","200","300","end"]

参考までに、このプログラムではどこにもto_iが使われていない。このため配列内に代入された値を見ると"100"というように""が付いている。""がついているのは文字列であり、このままでは全てを足して合計を算出するということはできない。

[5] インデックスを変数にして配列内の値を表示する

配列に値を代入する際にインデックスに変数を指定したように、配列に代入されている値を表示する際にもインデックスを変数にすることができる。

例えば、item = [100,200,300]というように3つの商品の金額が代入されていたとする。ここで、3つの商品の金額を表示する場合、array.rbの書き方をそのままもってくれば

printf("1個目%d円、2個目%d円、3個目%d円\n",item[0],item[1],item[2])

となる。既に気づいているだろうが、これもこのままでは使い勝手が悪い。配列内の要素の数は3個とは限らないためである。ここでも配列のインデックスに変数を使用して、順番に値を表示してみよう。

while-endを用いた配列内の要素の取り出し

まずは、while-endを用いて配列内の値を取り出して使用する方法について確認してみよう(index2.rb)。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

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)

このプログラムでは、冒頭でitem = [100,200,300]として、配列変数itemに初期値を与えている。もちろん初期値を与えずに、プログラム内で順次代入して言っても構わない。これを実行すると以下の通りとなる。

sime{c11xxxx}% chmod +x index2.rb[Return]
sime{c11xxxx}% ./index2.rb[Return]
1個目     100円
2個目     200円
3個目     300円
合計は600円です

lengthメソッドについて

  • lengthメソッド:配列に対して、配列変数.lengthとすることでその配列の中に代入されている値の数を返してくれるメソッド。全く同じ働きをするメソッドとしてsizeメソッドもある。

このプログラムではインデックスに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の行が実行され、合計金額が表示される。

for-endを用いた配列内の要素の取り出し

上記プログラムをfor-endを使って書きなおした例を次に見てみよう(index3.rb)。

#!/usr/koeki/bin/ruby
# -*- coding: utf-8 -*-

item = [100,200,300]
x = 1
sum = 0

for i in item
  printf("%d個目     %d円\n",x,i)
  x += 1
end
printf("合計は%d円です\n",sum)

実行結果は先ほどと同じになる。for-endを用いた場合は、lengthメソッドを使用していない。まず、配列内のデータを取り出す際のfor-endの用法を確認しよう。

for 変数 in 配列
   繰り返したい処理
end

これが、基本構造である。配列内の要素を先頭から順番に取り出し変数に代入しながら繰り返し処理を行う。全ての値を取り出したら繰り返し終了となる。

while-endのように繰り返し継続条件を考える必要がないので、配列内の要素の取り出しは、for-endを用いた方が楽である。後期になるとfor-endを頻繁に使用するようになるので、今のうちから使う練習をしておくと良い。

[6] 出席課題

index1.rbindex2.rbを合体しindex.rbを作成せよ。つまりキーボードから入力した値を配列変数に代入し、入力した値と合計を表示できるプログラムを作成せよ。

2つのプログラムを合体するにあたり、不要な部分としてindex2.rbの以下の3行を削除せよ。その上でindex1.rbの下にindex2.rbを貼りつける。

  • #!/usr/koeki/bin/ruby
  • # -*- coding: utf-8 -*-
  • item = [100,200,300]

これで実行してもエラーが出るので、エラーが出る理由を考えて解決してみよう。

制限時間は10分。完成しない場合は、途中まででも構わないので実行し、結果をメールで送ること。出席点は2点。提出要領は下記の通り。

  • 提出先:課題提出用メールアドレス
  • メールのSubject:ruby05
  • 本文の構成:1行目で学籍番号、氏名を記載する。2行目以降に改良したプログラムとktermでの実行結果を貼り付ける。その後感想や気がついた点を述べる。

[7] 二次元配列

配列の個々の要素にさらに配列を代入することができる。これを二次元配列という(通常の配列は一次元配列)。

hoge = []
hoge[0] = [1,2,3]
hoge[1] = [4,5,6]

とした場合、p hogeをすると[[1,2,3],[4,5,6]]となる。hogeの第0要素、第1要素いずれも配列が代入されている。二次元配列において各値の指定はhoge[i][j]という形をとる。

したがって、hoge = [[1,2,3],[4,5,6]]の場合、各値は以下のように指定できる。

  • 1:hoge[0][0]
  • 2:hoge[0][1]
  • 3:hoge[0][2]
  • 4:hoge[1][0]
  • 5:hoge[1][1]
  • 6:hoge[1][2]

[8] 配列の生成と配列処理メソッド

>>More

[9] レポート課題

1番、2番のいずれかを選んで実施せよ。なお、1番は授業で話した内容のみで実施可能だが、2番は[8] 配列の生成と配列処理メソッドや教科書を参考に自分で勉強をする必要がある。

  1. 前回作成したreport3.rbをさらに改良し、以下の要件を満たすreport4a.rbを作成しなさい。
    • report3.rbは最後に合計、平均を表示したが、これに加えて毎回のレポートの得点を表示できるようにせよ(ここまでだと7点満点)。
    • 出席回数の入力を求め、1回あたり2点として、レポート点と合算して合計得点を算出、表示せよ。その上で、合計得点に応じて成績(秀~不可)を表示せよ(ここまでだと8点満点)。
    • 上の2つの改良を行った上で、(printfの桁ぞろえ出力を活用して)出力結果を整えて表示せよ(ここまでだと9点満点)。
  2. 全て実施した際の結果出力例(右端が綺麗に整うようにすること)

    sime{c11xxxx}% chmod +x report4.rb[Return]
    sime{c11xxxx}% ./report4.rb[Return]
    出席回数を入力してください
    14[Return]
    1回目のレポートの得点を入力してください
    7[Return]
    2回目のレポートの得点を入力してください
    6[Return]
            :
           (略)
    
    +---------------------+
      出席点         28点 
    +---------------------+
      レポート            
      1回目         7.0点 
      2回目         6.0点 
      3回目         6.5点 
      4回目         8.0点 
      5回目         7.5点 
      6回目         6.0点 
      7回目         7.0点 
      8回目         7.0点 
    +---------------------+
      合計           83点 
      レポート平均  6.9点 
      評価           優
    +---------------------+
  3. test.datは3教科のテストの合計得点(300点満点)の100人分の架空データである。このデータを読み込み、上位20位まで
    1位  ○点
    2位  ○点
      :   
    というように、順位と得点を表示するプログラムreport4b.rbを作成しなさい。配列に代入した値を大きい順に並び替え繰り返しを20回行いながら順位と得点を表示する。100位まで全て結果を表示した場合は減点とする。結果は右端がきれいに整うように出力すること(9点満点)。

提出要領

  • 提出先:課題提出用メールアドレス
  • 提出期限:第1提出期限、第2提出期限を設定
  • メールのSubject:report04
  • 本文の構成:1行目で学籍番号、氏名を記載する。2行目以降は下記の構成とする
    1. 実施した課題の番号
    2. 作成したプログラム
    3. プログラムの実行結果
    4. プログラムの説明
    5. 感想
    6. 参考文献

採点要領

  • 採点基準:期限内提出点(2点)、メールの体裁(1点)、プログラム(2~4点)、プログラムの説明(2点)
  • 実行結果は1回分を貼りつけるだけでなく、ブラックボックステストやホワイトボックステストを行い、間違いがないことを確認すること。
  • プログラムの説明は、配列への代入部、配列からの読み出し部について重点的に行うこと。
  • 他人のレポートを丸写しした場合は、写した側、写させた側共に0点とする。
  • わかりにくい説明や、Webページを単にコピー&ペーストしただけの説明は減点する。一度読み直してから提出すること。
  • 驚異的に良くできているレポートについては満点を超える得点をつけることがある。
  • よくできていたレポートは、他の人の参考になるよう、本人が特定できないような形で掲載する。掲載してほしくない場合はメールでの課題提出時にその旨記載すること。