LoginSignup
7
4

More than 3 years have passed since last update.

マルチスケール特論 第10回

Last updated at Posted at 2020-11-25

ruby-2.7.1p83

fibonacci

rubocop にかかれば以下のコードも

require './assert_equal'

def fib(n)
  if n==0
    return 0
  end
end

puts assert_equal(0, fib(0))

良い感じに整形してくれます :)

# frozen_string_literal: true

require './assert_equal'

def fib(n)
  0 if n.zero?
end

puts assert_equal(0, fib(0))

return の挙動

fibonacci に n==1の場合を追加する。

# frozen_string_literal: true

require './assert_equal'

def fib(n)
  0 if n.zero?
  1 if n == 1
end

puts assert_equal(0, fib(0))
puts assert_equal(1, fib(1))

しかし、これだとテストに失敗する

expected: 0
result:   
failed in assert_equal.

expected: 1
result:   1
succeeded in assert_equal.

最後の行のみ return が暗黙に付加される仕様なのかな?n に 0 が渡されたときは、最初の行がreturn されることなく、次の行で nil が返されてるっぽい。というわけで、素朴に以下のように書いてみる

# frozen_string_literal: true

require './assert_equal'

def fib(n)
  if n==0
    return 0
  end
  if n==1
    return 1
  end
end

puts assert_equal(0, fib(0))
puts assert_equal(1, fib(1))

rubocop を通すと、

# frozen_string_literal: true

require './assert_equal'

def fib(n)
  return 0 if n.zero?

  1 if n == 1
end

puts assert_equal(0, fib(0))
puts assert_equal(1, fib(1))

うーん、なんか汚くね?というわけでcase文つかってみる。

# frozen_string_literal: true

require './assert_equal'

def fib(n)
  case n
  when 0
    0
  when 1
    1
  end
end

puts assert_equal(0, fib(0))
puts assert_equal(1, fib(1))
expected: 0
result:   0
succeeded in assert_equal.

expected: 1
result:   1
succeeded in assert_equal.

OK.こういうとき、テストのありがたさを実感する。

n<=2

case 文は値を評価するのであって、n <= 2 みたいな式には対応できない。ので以下のように書くとテストに失敗する。(bool 値を評価しているから、のはず)

# frozen_string_literal: true

require './assert_equal'

def fib(n)
  case n
  when 0
    0
  when n <= 2
    1
  end
end

[[0, 0], [1, 1], [2, 1]].each do |pair|
  puts assert_equal(pair[0], fib(pair[1]))
end

というわけで以下に直す。

# frozen_string_literal: true

require './assert_equal'

def fib(n)
  return 0 if n.zero?

  return 1 if n <= 2
end

[[0, 0], [1, 1], [2, 1]].each do |pair|
  puts assert_equal(pair[1], fib(pair[0]))
end

これ、最初の行の if が false なら return そのものが実行されないってなかなか直感に反するなぁ。if が false なら returnがなかったことになるってことか…?

完成

以下が完成形。

# frozen_string_literal: true

require './assert_equal'

def fib(n)
  return 0 if n.zero?

  return 1 if n == 1

  return fib(n - 1) + fib(n - 2)
end

[[0, 0], [1, 1], [2, 1], [3, 2], [4, 3], [5, 5], [6, 8], [7, 13], [8, 21]].each do |index, expected|
  puts assert_equal(expected, fib(index))
end

でもrubocopに通すと fib の最後の return をなくされる。

# frozen_string_literal: true

require './assert_equal'

def fib(n)
  return 0 if n.zero?

  return 1 if n == 1

  fib(n - 1) + fib(n - 2)
end

[[0, 0], [1, 1], [2, 1], [3, 2], [4, 3], [5, 5], [6, 8], [7, 13], [8, 21]].each do |index, expected|
  puts assert_equal(expected, fib(index))
end

規則性が乱れるような感じで、やや気持ち悪いのだが、rubocop が言うならしゃーない…か

assert_equal の class化

発展問題

# frozen_string_literal: true

require 'colorize'

class String
  def assert_equal(n)
    to_i == n
  end

  def assert_not_equal(n)
    to_i != n
  end
end

class Integer
  def assert_equal(n)
    self == n
  end

  def assert_not_equal(n)
    self != n
  end
end

if $PROGRAM_NAME == __FILE__
  p '3'.assert_equal(3)
  p 4.assert_equal(4)
end
% ruby assert_equal_class.rb
true
true

  • source ~/ghq/github.com/TeamNishitani/grad_members_20f/members/syasin-5d/10th.org
7
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
4