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