2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

チャート式ruby-V(Recursive Fibonacci)

Last updated at Posted at 2020-11-10

お題:Fibonacci数列

チャート式Rubyの5回目,recursion(再帰)です.

fib(0) = 0
fib(1) = 1
fib(n) = fib(n-1)+fib(n-2) for n>=2
0 1 1 2 3 5 8 13 21 ...
index n 0 1 2 3
val fin(n) 0 1 1 2

をrecursion(再帰)で求めなさい.

解説

以下はTDDのバイブルのKent Beck本1に載っていたののアレンジです.chart式の解法,解説,発展のサイクルがめちゃくちゃ短くなって,TDDのred, green, refactoringに対応している感触を掴んでください.

前回作ったassert_equal.rbをcodes(作業directory)にcopyしておいてください.

fib(0) = 0

Fibonacci数列の初項は0です.

  • red: まずは表示させてみます.

    p fib(0)
    

    ないよね.

  • green: そこで,defします.

    def fib(n)
      if n==0
        return 0
      end
    end
    
  • refactoring: assert_equal.rbのassertion(確認)を試しておきます.

    require './assert_equal'
    puts assert_equal(0, fib(0))
    

fib(1) = 1

次はfib(1)=1

  • red: まずはassertion
puts assert_equal(1, fib(1))

もちろん失敗するが...

  • green: コピペ.
def fib(n)
  if n==0
    return 0
  end
  if n==1
    return 1
  end
end

これで通る.

  • refactoring: テスト側の重複が気になってきたので,配列にしよう.
[[0,0],[1,1]].each do |pair|
  puts assert_equal(pair[0], fib(pair[1]))
end

テストはもちろんgreen.

refactoring

ここらへんまで来るとcodeがごちゃついてきた感じしません?しない? それは頭のいい人.私には無理.えっと,rubocopという汚いcodeの取り締まり警官は10行以上のmethodがあると,烈火のごとく怒ります.

治しようがないと思わずに削減を考えます.求めよさらば与えられん.そんなに無理しなくても,

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

と2行に削減できます.この記法も英語的でしょ.それがrubyのいいところ.これだと何をしているのか一目瞭然でしょ.動いた部分を隠すというのがありますが,こっちの方がスマートです.だからrefactoring(因数分解をもう一度)って名付けられています.

さらによく見ると

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

と一行になることにも気がつくはず.

fib(2) = 1

red

まずはテスト

[[0,0],[1,1],[2,1]].each do |pair|

なんですが,これはred.

> ruby fibonacci_2.rb
expected:0
result :0
succeeded in assert_equal 0 should be expected.

expected:1
result :1
succeeded in assert_equal 1 should be expected.

expected:2
result :1
failed in assert_equal 1 should be expected.

green

returnを増やして,

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

これで動くはず… おやおや.

実は,assert_equalは(expected, actual)とうけとっています.

result = fib(2)
expected = 1

だから,テストは配列変数pairの示数indexが逆で,

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

が正解.そうするとgreen.

refactoring

もう少し配列の受け取りを明示的にすると,

index, expected = pair

と修正できて

require './assert_equal'
[[0,0],[1,1],[2,1]].each do |index, expected|
  puts assert_equal(expected, fib(index))
end

の方がいいかも,圧倒的にいい〜〜.

fib(2) = 1 = fib(0) + fib(1)

red & green

人間は賢いから上のように書いても納得するけど,computerはだめ.sourceは

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

でまずはgreenにしています.

refactoring

そのあとでcodeを修正します.return 1の意味を考えると

return 0+1

ですよね.さらには

return fib(0) + fib(1)

というのも気がつくはず.そうするとcodeは

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

です.これで通るのが不思議かもしれませんが,とおりますし,意味もokです.

fib(3) = fib(1) + fib(2) = 1 + 1 = 2

red & green & refactoring = magic

次は3項目. 期待値は2ですね.

[[0,0],[1,1],[2,1],[3,2]].each do |index, expected|

としましょう.テストはもちろんredです.最後のreturnを定義通り

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

と修正しましょう.

そうするとあら不思議.通ります.そのさき1,1,2,3,5,8,13,21ももちろん.

最終形は,

sample
def fib(n)
  return n if n<=1
  return fib(n-1) + fib(n-2)
end

require './assert_equal'
[[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

です.

まとめ

うーーーん.こんなに綺麗にFibonacciを説明できるなんて.TDD万歳!!2

参照文献


  • source ~/git_hub/ruby_docs/chart_style_ruby/c5_fibonacci.org
  1. テスト駆動開発, Kent Beck (著), 和田 卓人 (翻訳), オーム社; 新訳版 (2017/10/14).

  2. 「TDD死すとも,testingは死せず」"TDD is dead. Long live testing." by David Heinemeier Hanssonってのもありますが...

2
0
0

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
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?