練習問題をリファクタリングするときのチェック項目(随時更新)

毎日ちょっとずつ練習問題を実施しては、自分で調べてリファクタリングをしています。

練習問題を毎日解いている理由は以下記事類を参照。

www.aoky.net

qiita.com

qiita.com

以下はその備忘録です。

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

ハッシュにブロックを与えると、対応する値がないキーが呼び出されるたびにブロックを評価する仕組みを使う。

docs.ruby-lang.org

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