Hatena::Grouphaskell

(200) Days of Haskell - Ruby 厨だけどハスケルやるよ

2011-10-13

(7) 素因数分解

| 17:20

module Main where
    import System
    isPrime :: Int -> Bool
    isPrime n | n <= 1    = False
              | n == 2    = True
              | otherwise = all (/= 0) $ map (mod n) $ enumFromTo 2 $ toUpperSqrt n
                          where
                              toUpperSqrt = ceiling . sqrt . fromIntegral

    enumPrimes :: Int -> [Int]
    enumPrimes n = filter isPrime $ (enumFromTo 1 n)

    minPrime :: Int -> Int
    minPrime n = (filter (\x -> n `mod` x == 0) $ enumPrimes(n)) !! 0

    factor :: Int -> [Int]
    factor 1 = []
    factor n = [minPrime(n)] ++ (factor $ n `div` minPrime(n))

    main = do
        n <- getArgs
        (putAll $ factor $ read $ n !! 0) >> putStrLn ""
            where
                putAll = mapM_ (putStrSpace . show)
                putStrSpace str = putStr $ str ++ " "

isPrime の自作にすごい苦労した……。

以下はコンパイルが通るが、動かない。

module Main where
    isPrime n | n <= 1    = False
              | n == 2    = True
              | otherwise = all (/= 0) $ map (mod n) $ enumFromTo 2 $ toUpperSqrt n
                          where
                              toUpperSqrt = ceiling . sqrt
Prelude> :load "isprime.hs"
[1 of 1] Compiling Main             ( isprime.hs, interpreted )
Ok, modules loaded: Main.
*Main> isPrime 1234567

<interactive>:1:1:
    Ambiguous type variable `a0' in the constraints:
      (RealFrac a0) arising from a use of `isPrime'
                    at <interactive>:1:1-7
      (Num a0) arising from the literal `1234567' at <interactive>:1:9-15
      (Integral a0) arising from a use of `isPrime'
                    at <interactive>:1:1-7
      (Floating a0) arising from a use of `isPrime'
                    at <interactive>:1:1-7
    Probable fix: add a type signature that fixes these type variable(s)
    In the expression: isPrime 1234567
    In an equation for `it': it = isPrime 1234567

これは、 modsqrt とで取るべき型が違うためだ。

*Main> :t mod
mod :: Integral a => a -> a -> a
*Main> :t sqrt
sqrt :: Floating a => a -> a

明示的に Floating に変換すればOK。 http://www.sampou.org/haskell/tutorial-j/numbers.html も参照せよ。

module Main where
    isPrime n | n <= 1    = False
              | n == 2    = True
              | otherwise = all (/= 0) $ map (mod n) $ enumFromTo 2 $ toUpperSqrt n
                          where
                              toUpperSqrt = ceiling . sqrt . fromIntegral

こうであれば、どっちも Integral を受け入れる。

$ ./factor 11223344
2 2 2 2 11 43 1483 

大丈夫です、 Haskell モヒカン族の皆さんにコメントされるまでもなく、

リスト内包表現、 1 : [2, 3, 4] のようなリストをくっつける表現などが身についてないことが分かりますね、、、

10 倍綺麗な書き方。

pi8027pi80272011/10/14 17:15(!!) は失敗するケースがあるので、より正しさが自明である書き方をしたい。と考えると、

factorization :: Int -> [Int]
factorization = factorization' 2

factorization' :: Int -> Int -> [Int]
factorization' _ 1 = []
factorization' n m
| m `mod` n == 0 = n : factorization' n (m `div` n)
| otherwise = factorization' (succ n) m

udzuraudzura2011/10/14 17:20なるほど、すごく綺麗ですね……。

udzuraudzura2011/10/14 17:20僕の書いた感じだとまだまだ手続き型っぽいオーラが出てます…