さらなる高みへ -higher ground -

プログラミング学習の定着を狙いとしたアウトプット(独り言)をしてみるブログ

配列:レビューを複数書ける追加機能

ループ処理によってでメニューがループするようになり、何回でもレビューを書けるようになった。

しかし今のままではレビューを書くたびに上書きされてしまう。
今回は複数のレビューをかけるようにするためのポスト。

今回はこの2点の実装を行う。

  • 配列が使えるようになる
  • 配列に要素を追加できるようになる

複数のレビューを扱う方法として考えられるのがレビューの数だけ変数を用意する方法である。
しかし、このアプリケーションでは書くことのできるレビューの数に限界がないため、いくつ変数を用意すれば良いか分からないうえ、その上限を超えられてしまったら対応することが出来ない。

そこで必要となるのが配列オブジェクトである。

配列オブジェクト

配列オブジェクトはハッシュと同じように、1つの変数でたくさんの情報を持つことのできるオブジェクトである。
配列の中には多くのオブジェクトを入れることができる=箱のようなものである。
ハッシュがキーでオブジェクトを管理していたのに対して、配列は順番でオブジェクトを管理する。

また、配列にデータを入れるとそのデータに順番がつけられる。すなわちデータと順番が紐づけられる。

要素

配列の中のデータは要素と呼ばれる。
要素は順番を持っていて、配列に入れた順に1番目,2番目..と自動的に順番が割り振られる。

イメージは下記の図である。

配列と順番

配列オブジェクトの生成

配列オブジェクトの生成は下記のようなコードで行う。

配列オブジェクト = []

これで何も入っていない空の箱の配列ができる。
注意点としては、ハッシュは{}(中括弧)で囲んでいたのに対し、配列は[](大括弧)で囲むという点である。

当然最初から何かを入れた状態で配列オブジェクトを生成することもできる。

  swallows_members = ["山田", "青木", "村上"]
  puts swallows_members
配列オブジェクトに追加する時
配列オブジェクト << 追加する要素

という記述をする。
先ほどの配列オブジェクトに要素を追加するには<<メソッドを使って、以下のように追加ができる。

  swallows_members = ["Yamada", "Aoki", "Murakami"] 
swallows_members << "Okugawa"
puts swallows_members

配列の要素の数をカウントする

配列の中にある要素の数をカウントしたい場合、lengthメソッドを利用する。

  swallows_members = ["Yamada", "Aoki", "Murakami"] 
  puts swallows_members.length

  swallows_members << "Okugawa"
  swallows_members << "Escobar"
  puts swallows_members.length

このようなコードで試してみると最初の返しは3、次の返しは5という数値になることが確認できる。

レビュー情報を配列オブジェクトで管理

ここまで書いてきたreviewApp.rbを配列を使って書き直してみる。
イメージとしては

  •  複数のレビューの情報を持った配列オブジェクト「posts」を定義する
  • 1つのレビューは今まで通り、ハッシュオブジェクト「post」で生成する
  • レビューを書いたらハッシュオブジェクト「post」を配列オブジェクト「posts」に追加する

という流れである。

postsとpost

これで、書いたレビューが次々に配列オブジェクトに入れられていく。

配列オブジェクトpostsを生成

新たに書き足すのはレビューを管理する配列オブジェクトpostsを生成する
posts = []の一行だけであるが、 問題はどこにこの一行を付け足すかである。

選択肢は3つある。

  • 繰り返し処理whileよりも前(while文の外)
  • whileで繰り返されている処理の中
  • どれかのメソッドの中(postReviewなど)

正解は繰り返し処理whileよりも前(while文の外)である。
なぜ他の2つではだめなのか。

まずwhileで繰り返されている処理の中だと、

while true do
  posts = []             # 配列オブジェクトpostsの生成
  # メニューの表示
  puts "レビュー数:0"
  puts "[0]レビューを書く"
  puts "[1]レビューを読む"
  puts "[2]アプリを終了する"
  input = gets.to_i

  if input == 0 then
    post_review         # post_reviewメソッドの呼び出し
  elsif input == 1 then
    read_reviews        # read_reviewsメソッドの呼び出し
  elsif input == 2 then
    end_program         # end_programメソッドの呼び出し
  else
    exception           # exceptionメソッドの呼び出し
  end
end

となるが、これではメニューが表示されるたびに配列が空になってしまう。

次に、どれかのメソッドの中(postReviewなど)であるが、これも結局posts = []を書いたどれかのメソッドが呼ばれるたびに配列が空になってしまう。

def post_review
  posts = []
  # 変数の定義
  post = {}
  puts "ジャンルを入力してください:"
  post[:genre]  = gets.chomp
  puts "タイトルを入力してください:"
  post[:title]  = gets.chomp
  puts "感想を入力してください:"
  post[:review] = gets.chomp
  line   = "---------------------------"

  # レビューの描画
  puts "ジャンル : #{post[:genre]}\n#{line}"
  puts "タイトル : #{post[:title]}\n#{line}"
  puts "感想 :\n#{post[:review]}\n#{line}"
end

# post_reviewを呼ぶたびに
# posts = []
# となり、配列が空になってしまう

よって、「プログラムが実行されてから一回しか呼ばれない場所」なのでwhile文よりも前、ということになる。

 # メソッドの定義
def post_review
  # 変数の定義
  post = {}
  puts "ジャンルを入力してください:"
  post[:genre]  = gets.chomp
  puts "タイトルを入力してください:"
  post[:title]  = gets.chomp
  puts "感想を入力してください:"
  post[:review] = gets.chomp
  line   = "---------------------------"

  # レビューの描画
  puts "ジャンル : #{post[:genre]}\n#{line}"
  puts "タイトル : #{post[:title]}\n#{line}"
  puts "感想 :\n#{post[:review]}\n#{line}"
end

def read_reviews
  # レビューを読む
end

def end_program
  exit
end

def exception
  puts "入力された値は無効な値です"
end

posts = []             # 配列オブジェクトpostsの生成

while true do
  # メニューの表示
  puts "レビュー数:0"
  puts "[0]レビューを書く"
  puts "[1]レビューを読む"
  puts "[2]アプリを終了する"
  input = gets.to_i

  if input == 0 then
    post_review         # post_reviewメソッドの呼び出し
  elsif input == 1 then
    read_reviews        # read_reviewsメソッドの呼び出し
  elsif input == 2 then
    end_program         # end_programメソッドの呼び出し
  else
    exception           # exceptionメソッドの呼び出し
  end
end

↑これが正解のコード

配列オブジェクトpostsを作ることができた。
あとは「レビューを書く」で生成したレビューのハッシュオブジェクトpostを配列postsに追加するだけである。

配列オブジェクトpostsにレビューの情報postを追加する

 ここまでで、配列オブジェクトpostsを作ることができた。
あとは「レビューを書く」で生成したレビューのハッシュオブジェクトpostを配列postsに追加するだけだ。

ここでreviewApp.rbの「レビューを書く」メソッドのpost_reviewを見てみよう。

def post_review
  # 変数の定義
  post = {}
  puts "ジャンルを入力してください:"
  post[:genre]  = gets.chomp
  puts "タイトルを入力してください:"
  post[:title]  = gets.chomp
  puts "感想を入力してください:"
  post[:review] = gets.chomp
  line   = "---------------------------"

  # レビューの描画
  puts "ジャンル : #{post[:genre]}\n#{line}"
  puts "タイトル : #{post[:title]}\n#{line}"
  puts "感想 :\n#{post[:review]}\n#{line}"
end

post_reviewメソッドの中では、新しいレビューの変数postを生成している。
これを配列postsに追加したいので、<<メソッドを使用する。

変数のスコープ

変数には、その変数が使える範囲というものが決まっている。
これをスコープという。
通常、メソッド内ではそのメソッド内で定義した変数しか利用することができない。

スコープ

ある変数を利用できる範囲のことである。
スコープの範囲外の変数を使おうとすると、エラーが起こる。
具体的には先ほどの例で、変数postsにとってpost_reviewメソッドがスコープ外である場合、エラーが起こる。

ではどのようにして変数のスコープが決まるのか。

変数のスコープの例以下のコードは、スコープが異なる範囲が存在しない。
こちらを実行すれば、2行目のputsによって問題なくターミナルに「東京」と表示される。

  • スコープの範囲内のため、正常に動作する
capital = "東京"             # 首都は「東京」
puts capital
  • スコープの範囲が違うため、エラーになる
capital = "東京"             # 首都は「東京」

def america
  puts capital
end

america                     # エラー

americaというメソッドを作って、その中で1行目の変数capitalをputsしようとしている。
しかし、メソッド内ではメソッド内で定義した変数しか使えない。

以下の例では、同名であるがスコープの範囲が違う場所でそれぞれ定義した変数の挙動を確かめられる。

  • 例:同名でもスコープの範囲が異なる変数
capital = "東京"             # 首都は「東京」

def america
  capital = "ワシントン"       # americaの首都は「ワシントン」
  puts capital
end

america                     # => ワシントン

メソッドamerica内4行目で変数capitalを生成している。
こちらは"ワシントン"という文字列である。

このファイルを実行すると、最終的には5行目のputsで"ワシントン"と表示される。
1行目で変数capitalという同じ名前で中身は"東京"の変数を定義しているが、メソッドamerica内では4行目につくった変数capitalが使われる。

違うスコープの変数を利用する

ここまでで、「メソッド内ではメソッド内で定義した変数しか使えない」ということを学んだ。
ではreviewApp.rbで「レビューを書く」メソッドのpost_review内で配列オブジェクトpostsを使うにはどうすれば良いか?

def post_review
  # 変数の定義
  post = {}
  puts "ジャンルを入力してください:"
  post[:genre]  = gets.chomp
  puts "タイトルを入力してください:"
  post[:title]  = gets.chomp
  puts "感想を入力してください:"
  post[:review] = gets.chomp
  line   = "---------------------------"

  # レビューの描画
  puts "ジャンル : #{post[:genre]}\n#{line}"
  puts "タイトル : #{post[:title]}\n#{line}"
  puts "感想 :\n#{post[:review]}\n#{line}"

  # 配列オブジェクトに追加
  posts << post
end

このコードではエラーを起こす。
18行目の配列オブジェクトpostsは、post_reviewメソッドの外で定義した変数なのでpost_reviewメソッド内では使えない。
「メソッドの外で定義した変数」を「メソッドの中」で使えるようにするには引数を使う。