Ruby ではじめるプログラミング 【第 3 回】

著者:だん

はじめに

前回 (Ruby ではじめるプログラミング 【第 2 回】) はいくつかの便利な機能と配列を中心に解説しました。

今回も Ruby の新しい機能を 2 つ紹介します。

  • ハッシュの使いかた
  • メソッド定義

ハッシュを使うと、前回配列を使って書いたプログラムがよりわかりやすく、便利になります。

より大きなプログラムを書くときに必要になるメソッド定義についても解説します。

ハッシュ

Ruby にはハッシュという機能があります。

ハッシュは数値以外も添え字に出来る配列のようなもので、配列を理解していればハッシュもすぐに使えるようになります。

ここではハッシュの使い方を解説します。

ハッシュの使い方

空のハッシュは次のようにして作ります。

values = {}

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

values = {}
p values

実行結果はこうなります。

{}

データを持つハッシュは次のようにして作ります。

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

データ全体を__ { } で囲んでいることに注意してください。配列は [ ] を使いましたがハッシュでは { } __を使います。

values の中身は複数行に分けて書きましたが、次のように 1 行で書くことも出来ます。

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

こう書いても意味はまったく同じです。

p メソッドを使って values の中身を表示すると結果はこうなります。

{"MP"=>4, "STR"=>9, "HP"=>16}

値の追加と取り出し

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

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

# ひとつずつ表示
puts values['HP']
puts values['MP']
puts values['STR']
# values の中身を表示
p values

実行結果はこうなります。

16
4
9
{"MP"=>4, "STR"=>9, "HP"=>16}

このハッシュデータの ‘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]

このハッシュを使ったプログラムは配列を使ったじゃんけんプログラムと同じように動作します。

配列のデータは 0 からはじまる添え字を指定することで取り出すことができました。ハッシュの場合は添え字にあたるものを自分で決めることができます。上のプログラムでは__ => の左側にある 0,1,2 がそれにあたります。この => __の左側にある 0,1,2 が__キー__です。

つまりこのプログラム中のハッシュ values では、

  • キーが 0 なら値は「グー」
  • キーが 1 なら値は「チョキ」
  • キーが 2 なら値は「パー」

というデータが格納されているわけです。

ハッシュの__キーと値は必ずペア__になります。ハッシュはキーと値のペアが集まったオブジェクトということもできます。

ハッシュのキーには便利なことにどんなオブジェクトでも使うことができます。

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

ハッシュの使い方に続いて、ここではハッシュの便利さについて解説します。

前回紹介した配列を使ったゲームブックプログラムをハッシュを使って書き直してみます。配列とハッシュの違いを見てみましょう。

ハッシュ版ゲームブック

前回との違いは 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

このプログラムは前回の配列バージョンのゲームブックプログラムと見かけ上は全く同じように動作します。

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

今回わざわざハッシュを使って改造したこのプログラムには何かいいことがあったのでしょうか。

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

実行結果はこうなります。

Hello

引数を受け取るメソッド

メソッドには引数を受け取るものがありました。引数を渡すことのできるメソッドも簡単に作ることができます。

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

print_hello_name('太郎')

実行結果はこうなります。

Hello 太郎

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

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

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

tasizan(1, 2, 4)

実行結果はこうなります。

7

戻り値を返す

これまで使ってきたメソッドの中には値を返すものがありました。 例えば配列の size メソッドです。次のコードで size メソッドは 値「3」を返しています。

nums = [1, 2, 3]
p nums.size   #=> 3      ← 3 を返している

この、メソッドが返す値のことを「メソッドの戻り値」と言います。

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

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

print tasizan(1, 2, 4)

このプログラムでは 3 つの引数を足し算した合計の値が戻り値になっていて、その戻り値を tasizan メソッドの外の print メソッドが受け取って表示しています。 実行結果はこうなります。

7

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

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

print tasizan(1, 2, 4)

実行結果はこうなります。

7

次回は自作したメソッドを使うもっと大きなプログラムを紹介します。

まとめ

今回はハッシュの使い方と、メソッドの作り方を紹介しました。

ゲームブックプログラムではハッシュを使うことのメリットについて紹介しました。 全く同じように動作するプログラムでもテーブルデータの持ち方を少し変えるとデータの管理がとても楽になることがあります。 紹介したような少量のデータではたいした違いになりませんが、市販されるようなシナリオデータが巨大になるアプリケーションではこのような効率化がとても重要です。

プログラムを作るときには、なるべく人間の手間を軽減することや、ちょっとしたミスが起こりにくくなるように注意を払うことが大切です。

筆者について

だん (dan at dgames dot jp)

ゲームメーカーに勤めるゲームクリエイター。 Ruby を使ってオープンソースのゲームが開発できないかと模索中。

Ruby ではじめるプログラミング 連載一覧