毎日ちょっとずつ練習問題を実施しては、自分で調べてリファクタリングをしています。
練習問題を毎日解いている理由は以下記事類を参照。
以下はその備忘録です。
eachを配列として再利用するときはmapを使用する
問題
Hello World![改行]を5回表示させてください。 print(或いはprintf,cout等)を5回コピーすれば当然可能ですが、 ループ構文(for,while等)を利用して、print等は1回の使用にとどめてみてください。
eachで配列を返そうとすると、外に空の配列を用意して、入れていくとしなければいけない。
def repeat_hello(n) # ロジック本体 rows = [] n.times do |n| rows.push("Hello World! #{n + 1}") end end
これはmapを使うことで、スリムにできる。
def repeat_hello(n) # ロジック本体 (1..n).map{ |i| "Hello World! #{i}" } end
ifを後ろにつける、三項演算子をつける
問題
ルールは以下の通り 1から順番に数を表示する その数が3で割り切れるなら"Fizz"、5で割り切れるなら"Buzz"、両方で割り切れるなら"FizzBuzz"と表示する 要するに"1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz ・・・"と出力される
ifの分岐はendも含めて、縦に長くなりがち。
def fizzbuzz(num) (1..num).map { |i| if i % 15 == 0 "FizBuzz" elsif i % 5 == 0 "Buzz" elsif i % 3 == 0 "Fizz" else i end } end
例えばこんな風に、ifを後ろに持ってきたり、三項演算子を使うことで、一行にかける情報を増やせる。
def fizzbuzz(num) (1..num).map { |i| ret = '' ret = 'Fizz' if i % 3 == 0 ret += 'Buzz' if i % 5 == 0 ret.empty? ? i : "#{ret}" } end
明らかに除ける部分を先に処理する
問題
入力された整数がグレゴリオ暦(いつも使ってるやつ)でうるう年であるか判定せよ
ANDやORを使えば、簡単な論理構造の接続はシンプルに書けはするが、
def uruu(num) num % 400 == 0 || (num % 100 != 0 && num % 4 == 0) end
処理速度を測った所、こちらの方が良い。全体の3/4は4で割れないので、その時点でfalseになり、それ以上計算をする必要がない。
def uruu(num) return false if num % 4 != 0 return false if num % 100 == 0 && num % 400 != 0 true end
再帰関数はハッシュでメモ化する
問題
フィボナッチ数列の第n項を求めるプログラムを再帰呼出しを用いて書いて下さい。ただしnはコマンドライン引数で得るものとします。
require 'minitest/autorun' def fib(n) return 1 if n == 1 || n == 2 fib(n-1) + fib(n-2) end class FibTest < Minitest::Test def test_fib test_data = [ [1, 1], [2, 1], [3, 2], [4, 3], [5, 5], [6, 8], [7, 13], [8, 21] ] test_data.each do |id, expected| assert_equal expected, fib(id) end end end
ハッシュにブロックを与えると、対応する値がないキーが呼び出されるたびにブロックを評価する仕組みを使う。
require 'minitest/autorun' def fib(num) fib_hash = Hash.new do |h, n| if n < 2 n else h[n] = h[n-1] + h[n-2] end end fib_hash[num] end class FibTest < Minitest::Test def test_fib test_data = [ [1, 1], [2, 1], [3, 2], [4, 3], [5, 5], [6, 8], [7, 13] ] test_data.each do |id, expected| assert_equal expected, fib(id) end end end