Ruby で関数型プログラミングっぽく(コピペ) + Haskell 版

parrot.hatenadiary.jpここのブログ記事を読んで感銘を受けました。だからこれを読んでもらえればよいのですが、せっかくなのでコピペしておきます。元記事に感謝です。

まずは問題。

ある数字にそれを逆に並べた数字を足すという計算を、
回文数(上から読んでも下から読んでも同じ数)になるまで繰り返すとき、
もっとも計算回数を要する二桁の数を答えなさい
 
例:ab+ba=123の場合、123+321=444で回文数なので、2回となる

https://parrot.hatenadiary.jp/entry/20110302/1299051431

 
Ruby コードです。多少自己流に書き直しました。

reverse_num = ->(num) {num.to_s.reverse.to_i}
is_palindrome = ->(num) {num == reverse_num.(num)}
execute = ->(count, num) {
  r = num + reverse_num.(num)
  is_palindrome.(r) ? count : execute.(count + 1, r)
}

result = (10..99).map{|n| [n, execute.(1, n)]}
max_count = result.map(&:last).max
result.select{|r| r.last == max_count}.each{|r| puts "num:#{r.first} count:#{r.last}"}

結果。89 と 98 ですね。24回繰り返しています。

$ time ruby palindrome_num.rb
num:89 count:24
num:98 count:24

real	0m0.091s
user	0m0.076s
sys	0m0.012s

 
いや、すばらしい。
 

Haskell

頑張って Scala ならぬ Haskell で同様のことをやってみました。
palindrome_num.hs

reverseNum :: Int -> Int
reverseNum num = read $ reverse $ show num

isPalindrome :: Int -> Bool
isPalindrome num = (num == reverseNum num)

execute :: Int -> Int -> Int
execute count num = if isPalindrome r
                    then count
                    else execute (count + 1) r
                        where r = num + reverseNum num
                        
main :: IO ()
main = putStr $ unlines $ map toS $ [r | r <- result, last r == maxCount]
           where result = [[n, execute 1 n] | n <- [10..99]]
                 maxCount = maximum $ map last result
                 toS [a, b] = "num:" ++ show a ++ " count:" ++ show b

こんなのでいいのかな。
結果。

$ time ./palindrome_num
num:89 count:24
num:98 count:24

real	0m0.003s
user	0m0.000s
sys	0m0.000s

当然のことながら瞬殺ですな。

しかし、Ruby でも結構関数型っぽく書けますね。ほとんどそのまま Haskell になるじゃん。自分は Ruby で慣れているので、Ruby の方が見やすいくらいだ。以下の記事もよろしければどうぞ。
obelisk.hatenablog.com

 

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!