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

![ruby-2.7.1p83](<https://img.shields.io/badge/ruby-2.7.1p83-brightgreen>) 

# fibonacci

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

```ruby
require './assert_equal'

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

puts assert_equal(0, fib(0))
```

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

```ruby
# 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の場合を追加する。

```ruby
# 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 が返されてるっぽい。というわけで、素朴に以下のように書いてみる

```ruby
# 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 を通すと、

```ruby
# 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文つかってみる。

```ruby
# 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 値を評価しているから、のはず)

```ruby
# 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
```

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

```ruby
# 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がなかったことになるってことか&#x2026;?

## 完成

以下が完成形。

```ruby
# 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 をなくされる。

```ruby
# 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 が言うならしゃーない&#x2026;か

# assert\_equal の class化

[発展問題](https://qiita.com/daddygongon/items/969ad5112878f6dab844#%25E7%2599%25BA%25E5%25B1%2595%25E5%2595%258F%25E9%25A1%258C)

```ruby
# 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