🐕

【Ruby学習記録】さまざまな繰り返し処理

2022/09/11に公開

お題1:任意の数字を5つ配列の要素にして変数に代入し、その配列の要素の数字 1 つ1つに 10 を足した結果を配列で出力せよ

answer.rb
numbers = [21, 33, 54, 11, 99]

results =
  numbers.map do |number|
    number + 10
  end

p results
===
$ ruby answer.rb
[31, 43, 64, 21, 109]

Ruby で配列の全要素に対して何らかの処理を行って、その結果をあらためて配列で受け取りたい場合に、mapメソッドが使える。

なお、mapメソッドのように、処理結果をあらためて配列で返せるようなメソッドはいくつかあるが、基本的には「do ~ endのブロック内の最後の式の戻り値が新たな配列の要素として返る」という形になる。

このお題の場合は、number + 10という計算式の結果が配列に格納される形になる。

お題2:下記の年を西暦で表した整数を格納している配列から、うるう年のみを取り出し、その結果を配列で出力せよ

example.rb
years = [1982, 1985, 1988, 1994, 2000, 2010, 2012, 2015, 2018, 2020]

なお、うるう年の定義は下記の条件で判定している。

  • 4 の倍数の年
  • 100 の倍数の年は除外
  • 400 の倍数の年はうるう年
answer.rb
years = [1982, 1985, 1988, 1994, 2000, 2010, 2012, 2015, 2018, 2020]

leap_years =
  years.select do |year|
    if year % 4 == 0  # cond.1
      if year % 100 == 0 && year % 400 != 0  # cond.2
        false    # case.2
      else
        true    # case.3
      end
    else
      false    # case.1
    end
  end

p leap_years
===
$ ruby answer.rb
[1988, 2000, 2012, 2020]

今回のように、元の配列から「条件を満たすもの」だけを抜き出した配列を作りたい場合に、selectメソッドが使える。
selectメソッドの場合は、do ~ endのブロック内の処理が最終的にtrueとなった場合のみ、処理中の要素を新たな配列の要素として戻してくれる。

なので、今回はdo ~ endのブロック内の処理で、対象の年が「うるう年かどうか」を判定し、うるう年ならtrue、そうでなければfalseとなるようにしている。

なお、うるう年の判定のように、何段階かのステップを踏んで判定しないといけないケースは実務でもよく出てくるが、そういった場合は、

  • キチンとフローチャートなどで条件と結果を整理する
  • 実装後のテストはキチンと全部の分岐ルートを辿るようなテストケースを実施する

というのが非常に大事になる。特にテストケースは大事。

今回のお題であれば、まずうるう年判定部分のフローチャートは以下のようになる。

それを踏まえて、テストケースを考える。実際の分岐では case.1 ~ case.3 の3パターンあれば良さそうに見えるが実際はもう少し必要。
結論からいくと、下記の 4 パターンが必要になってくる。

  1. 4 の倍数ではない値 ... case.1
  2. 4 の倍数だが、100 の倍数でも 400 の倍数でもない値 ... case.3
  3. 100 の倍数かつ、400 の倍数ではない値 ... case.2
  4. 400 の倍数 ... case.3

case が 3 つならテストケースも 3 つで良いように思えるが、今回は cond.2 の部分が 2 つの比較式になっているのでやや複雑になる。

if 文としては 1 つにまとめられたとしても、2 つの比較式を使っている場合、実際のパターンとしては下記の 4 パターン存在する。

  • 条件 1 が真・条件 2 が真
  • 条件 1 が真・条件 2 が偽
  • 条件 1 が偽・条件 2 が真
  • 条件 1 が偽・条件 2 が偽

これを今回のケースに当てはめた場合、cond.2 の部分で取りうるパターンが下記のようになるので、それに応じて 3 パターンのテストケースが必要になるという具合。

  • 100 の倍数である・400 の倍数である ... case.3
  • 100 の倍数である・400 の倍数でない ... case.2
  • 100 の倍数でない・400 の倍数である ... これは数値としてありえないので除外
  • 100 の倍数でない・400 の倍数でない ... case.2

そう考えると、実は今回のお題の場合、case.2 に該当する値がなかったりするので、テストケースとしては不十分。
最終的には配列の値は出題時の状態に戻すが、テストとしては"1900"あたりの 100 の倍数で 400 の倍数でない値も入れてテストを行わないといけない。

お題3:好きな文字列を 10 回繰り返して puts で出力した後、数字の 10 からカウントダウンで 0 まで puts で出力せよ

string = "Never up, Never in."

10.times do
  puts string
end

10.downto(0) do |index|
  puts index
end

Ruby には一応、for 文や while 文もあるにはあるが、あんまり主流ではないらしい。
というのも、Integer(整数)クラスのインスタンスメソッドにforの代わりに使える、timesdowntoメソッドが存在しているため。

C あたりの古い言語のスタイルに慣れてると、変数でもなんでもない固定の値に直接メソッド使えるのが違和感が生じるんだが、「Ruby は全てオブジェクト」ということなので、ただの"10"という数値であっても、それは Integer クラスのインスタンス、という扱いになる。

なので、Integer クラスのインスタンスメソッドであるtimesdowntoが使えて、それを使えば、従来は for 文を使っていたような固定回数の繰り返しが出来ちゃうよ、というお話。

GitHubで編集を提案

Discussion