プログラミング入門(3)

下記のページを参考にプログラミングの勉強を行います。
Rubyist Magazine - Ruby ではじめるプログラミング 【第 3 回】

ハッシュ


ハッシュは数値以外も添え字にできる配列のようなもの。

ハッシュ


空のハッシュは次のように作る。

values = {}

pメソッドで中身を表示することもできる。

values = {}
p values

f:id:ayaketan:20111209114059j:image

データを持つハッシュは次のように作る。

values = {
  'HP' => 16,
  'MP' => 4,
  'STR' => 9
}

データ全体を{}で囲んでいる。配列は[]を使ったが、ハッシュでは{}を使う。
valuesの中身は複数行に分けて書いたが、次のように1行で書くこともできる。

values = {'HP' => 16, 'MP' => 4, 'STR' => 9}

pメソッドを使ってvaluesの中身を表示する。

values = {
  'HP' => 16,
  'MP' => 4,
  'STR' => 9
}
p values

f:id:ayaketan:20111209114126j:image

値の追加と取り出し


次のプログラムは、ハッシュに値を追加し、その値を表示するプログラム。

values = {}
# ハッシュに値を追加
values['HP'] = 16
values['MP'] = 4
values['STR'] = 9

# ひとつつずつ表示
puts values['HP']
puts values['MP']
puts values['STR']

# values の中身を表示
p values

f:id:ayaketan:20111209114149j:image
このハッシュデータの'HP','MP','STR'をキーと呼び、それに関連付けられた16,4,9を値と呼ぶ。
このハッシュではキーに文字列オブジェクト、値に数値オブジェクトを用いている。
配列の添え字は数値しか使うことができなかったが、ハッシュでは配列の添え字に当たる部分に文字列を使うこともできる。

ハッシュは配列に似ている


ハッシュの使い方は配列によく似ている。ハッシュは連想配列と呼ばれることもある。
次のプログラムは前回勉強したじゃんけんロボット。

puts 'じゃんけん'
sleep 1
values = ['グー', 'チョキ', 'パー']
r = rand(3) # r に 0 〜 2 の乱数を代入
puts values[r]

このプログラムではvaluesが配列になっているが、このvaluesはハッシュを使って書くこともできる。

puts 'じゃんけん'
sleep 1
values = {
  0 => 'グー',
  1 => 'チョキ',
  2 => 'パー'
}
r = rand(3) # r に 0 〜 2 の乱数を代入
puts values[r]

f:id:ayaketan:20111209114225j:image

ハッシュのキーと値は必ずペアになる。
ハッシュのキーにはどんなオブジェクトも使うことができる。

ハッシュを使ったゲームブック


前回勉強した配列を使ったゲームブックをハッシュを使って書きなおす。

ハッシュ版ゲームブック


前回との違いは14行目からのtblへ代入されるオブジェクトがハッシュオブジェクトに変わっている点と、23行目のscene変数の初期値が'opening'という文字列オブジェクトの代入になっている点。
この'opening'がハッシュオブジェクトであるtblに含まれるキーになっている。
24 行目以降は配列バージョンのプログラムとまったく同じ。

msg0 = "3本の分かれ道があります。どの道を進みますか。\n" +
       "  1 左の道\n  2 真ん中の道\n  3 右の道"
msg1 = "あっ!\n落とし穴に落ちてしまいました。\n〜 GAME " +
       "OVER 〜"
msg2 = "真ん中の道をまっすぐ歩いていくと……\n宝箱をみつ" +
       "けました!\n  1 そのままにしておく\n  2 あける"
msg3 = "しばらく歩き続けると もとの場所にもどってしまい" +
       "ました。\n  1 次へ"
msg4 = "宝箱には見向きもせず お家に帰りました。\n〜 GAM" +
       "E OVER 〜"
msg5 = "パカッ\nまばゆい光があふれだす……\n100枚の金" +
       "貨を手に入れました!"

tbl = {
  'opening' => [msg0, 'left', 'center', 'right'],
  'left'    => [msg1],
  'center'  => [msg2, 'leave', 'ending'],
  'right'   => [msg3, 'opening'],
  'leave'   => [msg4],
  'ending'  => [msg5],
}

scene = 'opening'
while true
  scene_data = tbl[scene]
  message = scene_data[0]
  puts message

  if scene_data[1] == nil
    exit
  end

  print '  数字を入力してください '
  input_value = gets.to_i

  if input_value > 0
    next_scene = scene_data[input_value]
    if next_scene == nil
      puts '不正な値が入力されました'
    else
      scene = next_scene
    end
  else
    puts '不正な値が入力されました'
  end

  sleep 0.5
  print "\n"
end

f:id:ayaketan:20111209114256j:image

配列バージョンより便利になったこと


scene変数を使って場面を管理しているが、これは配列バージョンもハッシュバージョンも同じ。
ただ、今回のハッシュ版ではscene変数に代入される値が数値から文字列オブジェクトに変わっている。
配列版ではsceneには数値オブジェクトが代入されていたが、各場面に割り当てられていた数値は、たまたまその場面情報が配置されている配列内の位置によって決まっていた番号で、数値と場面情報の間には本質的な関係がなかった。
ハッシュ版では場面をイメージできるような意味のある単語を文字列としてキーにすることができる。
キーを見れば、ただの数値よりも、そのキーがどの場面を示すものなのかが直感的に把握できるようになる。

また、このプログラムではテーブルデータを書き換えることで新しい場面を追加したり、削除したりすることができるが、配列版のテーブルは安易にデータを書き換えると問題が発生することがある。
問題が起こるのは場面データの並び順を変更した場合。
例えば、ある場面を配列から削除すると、それ以降のインデックスがずれるのでジャンプ先を指定する数値も書き換えなければならない。
これはとても面倒な作業だし、書き換えることをうっかり忘れてテーブルデータに矛盾を発生させてしまうかもしれない。
しかし、ハッシュ版ではこの心配がない。もともと場面ごとにその場面にふさわしいキーが文字列で振ってあり、そのキーを使って場面データを参照することになるのでテーブル内のデータを追加・削除することによってずれが生じるというようなことはない。

メソッド定義


メソッドを作る


これまではあらかじめRubyに用意されているメソッドを使ってきたが、メソッドは自分で作ることもできる。
たとえは「Hello」という文字列を表示するprint_helloという名前のメソッドを作るには次のようにする。

def print_hello
  print 'Hello'
end

これでprint_helloメソッドができた。
defの後に作成するメソッドの名前を書いて、メソッドの最後はendで閉じる。
メソッドな名前に使える文字はアルファベットと数字とアンダースコア(_)。
ただし、先頭の1文字目に数字を使うことはできない。
メソッドを作ることをメソッドを定義するという。
メソッドは定義しただけでは実行されない。
実行するにはこのメソッドを呼び出す必要がある。

# print_helloメソッドを定義
def print_hello
  print 'Hello'
end

# print_helloメソッドを呼び出す
print_hello

f:id:ayaketan:20111209114326j:image

引数を受け取るメソッド


引数を渡すことのできるメソッドも簡単に作ることができる。

def print_hello_name(name)
  print 'Hello'
  print name
end

print_hello_name('太郎')

f:id:ayaketan:20111209114345j:image
このprint_hello_nameは引数を 1 つ受け取るメソッドす。
先頭のprint_hello_nameの直後にある(name)の部分で引数を受け取ることを定義している。
渡された引数はname変数に代入されメソッドの中で使うことができる。

引数を2つ以上持つメソッドを定義したければ、引数をカンマ(,)で区切って並べる。

def tasizan(a, b, c)
  print a + b + c
end

tasizan(1, 2, 4)

f:id:ayaketan:20111209114404j:image

戻り値を返す


メソッドが返す値のことを「メソッドの戻り値」と言う。
戻り値を返すメソッドを定義することもでるす。
メソッドから戻り値を返すにはreturnを使用する。
return文が実行されるとreturnの直後に書かれた値が戻り値となる。

def tasizan(a, b, c)
  total = a + b + c
  return total
end

print tasizan(1, 2, 4)

f:id:ayaketan:20111209114501j:image
このプログラムでは3つの引数を足し算した合計の値が戻り値になっていて、その戻り値をtasizanメソッドの外のprintメソッドが受け取って表示している。

メソッドの中でreturnが呼ばれるとそこでメソッドの処理が終了してしまうので、それより後ろに書かれている処理は実行されない。

def tasizan(a, b, c)
  total = a + b + c
  return total
  print '表示されない' #この行は実行されない
end

print tasizan(1, 2, 4)

f:id:ayaketan:20111209114527j:image