QuickCheckでリファクタリング

30分プログラム、その562。QuickCheckでリファクタリング
ちょっと前に、Haskell使いのid:zyxwvが「QuickCheckはリファクタリングに使うといいと思うんだ。リファクタリング前と後の関数の挙動同じかチェックできるから」と言っていたことを唐突に思い出した。
というわけでやってみよう。

まずは元の関数

まずは元の関数を書いてみる。由緒正しい階乗計算の関数。

fact :: Int -> Int
fact 0 = 1
fact n = n * fact (n-1)

反復的に書き直す

次に、反復的に書き直す。これも由緒正しいコードだと思う。

fact_i 0 n = n
fact_i i n = fact_i (i-1) (n*i)

fact' :: Int -> Int
fact' n = fact_i n 1

そしてここからがおもしろい。QuickCheckで元の関数と挙動が同じかチェックする。

prop_fact' n = n >= 0 ==> fact n == fact' n
*Main> quickCheck prop_fact'
OK, passed 100 tests.

もう一回書き直す

Haskellだったら無限リストだよなぁ、と思って適当に書いてみた。たぶん再発明。

facts = 1:1:zipWith (*) [2..] (tail facts)

fact'' :: Int -> Int
fact'' n = facts !! n
prop_fact'' n = n >= 0 ==> fact n == fact'' n
*Main> quickCheck prop_fact''
OK, passed 100 tests.

ソースコード

import Test.QuickCheck

-- original
fact :: Int -> Int
fact 0 = 1
fact n = n * fact (n-1)

-- iter
fact_i 0 n = n
fact_i i n = fact_i (i-1) (n*i)

fact' :: Int -> Int
fact' n = fact_i n 1

-- stream
facts = 1:1:zipWith (*) [2..] (tail facts)

fact'' :: Int -> Int
fact'' n = facts !! n

-- test
prop_fact' n = n >= 0 ==> fact n == fact' n
prop_fact'' n = n >= 0 ==> fact n == fact'' n