【Ruby学習記録】さまざまな繰り返し処理
お題1:任意の数字を5つ配列の要素にして変数に代入し、その配列の要素の数字 1 つ1つに 10 を足した結果を配列で出力せよ
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:下記の年を西暦で表した整数を格納している配列から、うるう年のみを取り出し、その結果を配列で出力せよ
years = [1982, 1985, 1988, 1994, 2000, 2010, 2012, 2015, 2018, 2020]
なお、うるう年の定義は下記の条件で判定している。
- 4 の倍数の年
- 100 の倍数の年は除外
- 400 の倍数の年はうるう年
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 パターンが必要になってくる。
- 4 の倍数ではない値 ... case.1
- 4 の倍数だが、100 の倍数でも 400 の倍数でもない値 ... case.3
- 100 の倍数かつ、400 の倍数ではない値 ... case.2
- 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
の代わりに使える、times
やdownto
メソッドが存在しているため。
C あたりの古い言語のスタイルに慣れてると、変数でもなんでもない固定の値に直接メソッド使えるのが違和感が生じるんだが、「Ruby は全てオブジェクト」ということなので、ただの"10"という数値であっても、それは Integer クラスのインスタンス、という扱いになる。
なので、Integer クラスのインスタンスメソッドであるtimes
やdownto
が使えて、それを使えば、従来は for 文を使っていたような固定回数の繰り返しが出来ちゃうよ、というお話。
Discussion