Haskell再び、簡単なところから

Haskellは一旦あっさり諦めようと思ってたけど、結局、オライリーから出ている「Real World Haskll」という本を買ってしまって、また読み始めている。漆塗り勉強法。薄く何度も繰り返し塗る。

で、少し読み進めたらまた何か簡単なものなら書けそうな気がしてきて、以下の問題をHaskellで解いてみた。

「2乗した結果に1から9までの数字が1度ずつ現れる整数をすべて挙げよ」。

以前にRubyで書いたのは以下。

def has9digits? (num)
  exit if num.to_s.size >= 10
  digits = num.to_s.split(//).sort.join
  digits == "123456789"
end

1.upto(10000000) do |i|
  print i," * ",i," = ",i**2,"\n" if has9digits?(i**2)
end

結果は、

% ruby 9digi.rb
11826 * 11826 = 139854276
12363 * 12363 = 152843769
12543 * 12543 = 157326849
14676 * 14676 = 215384976
15681 * 15681 = 245893761
15963 * 15963 = 254817369
18072 * 18072 = 326597184
19023 * 19023 = 361874529
19377 * 19377 = 375468129
19569 * 19569 = 382945761
19629 * 19629 = 385297641
20316 * 20316 = 412739856
22887 * 22887 = 523814769
23019 * 23019 = 529874361
23178 * 23178 = 537219684
23439 * 23439 = 549386721
24237 * 24237 = 587432169
24276 * 24276 = 589324176
24441 * 24441 = 597362481
24807 * 24807 = 615387249
25059 * 25059 = 627953481
25572 * 25572 = 653927184
25941 * 25941 = 672935481
26409 * 26409 = 697435281
26733 * 26733 = 714653289
27129 * 27129 = 735982641
27273 * 27273 = 743816529
29034 * 29034 = 842973156
29106 * 29106 = 847159236
30384 * 30384 = 923187456

という感じ。

同じ発想で、Haskellでも書いてみた。

import List

main = do putStrLn $ format $ filter isNinedigit [1..100000]

isNinedigit :: Int -> Bool
isNinedigit n = if (x == "123456789") then True
                else False
                    where x = sort $ show (n * n)

format :: [Int] -> String
format (n:ns) = a ++ " * " ++ a ++ " = " ++ b ++ "\n" ++ (format ns)
                   where a = (show n)
                         b = (show (n * n))
format [] = ""

これでほぼ動いている。なぜか上限を突破して、おかしな数字が出つづける。

29034 * 29034 = 842973156
29106 * 29106 = 847159236
30384 * 30384 = 923187456
67759 * 67759 = 296314785
69041 * 69041 = 471692385
70121 * 70121 = 621987345
71467 * 71467 = 812564793

30384 * 30384 = 923187456までは確かに成立してるけど、67759 * 67759 = 296314785はおかしい。うーん。単純にIntが32ビットで桁溢れを起こしてるだけか。67759 * 67759 = 4591282081で45億だから溢れてるっぽい。

おお、「67759**2 - 0x100000000 => 296314785」だった。なるほど、確かに一巡してそうなってるわ。では、こういう場合、停止条件を書きたいんだけど、Haskellだとどうするんだろうか。あらかじめ2乗してそれが32ビット以下であることを前提にする関数を作るとか? そのとき、各数字の2乗の計算結果ってちゃんと再利用されるのかしら。

あ、パターンマッチで再帰してるところは何か書き間違えてる気がする。x:xs、x、[]の3種類が必要かな? あとで確認する。

数字を扱ったパズルとかだとRubyのようなFixnumとBignumを勝手に切り替えてくれる仕様はありがたいなぁ。