Programing in Haskell Chapter5 / Cracking the cipher

今日は暗号解読のプログラミングを書き写してみました。
Programing in Haskell(http://www.amazon.co.jp/dp/0521692695) P.38-46

import Data.Char as C
-- xと等しい要素をもつリストxsの位置情報を返す
positions      :: Eq a => a -> [a] -> [Int]
positions x xs =  [ i | (y, i) <- zip xs [0..n], x == y]
				  where n = length xs - 1

-- 小文字のaを基準にして、aとの相対位置を返す
-- a -> 0, b -> 1 ....
let2int   :: Char -> Int
let2int c =  C.ord c - C.ord 'a'

-- 1~25に応じて、数字を小文字アルファベットに返す
-- 0 -> a, 1 -> b ...
int2let   :: Int -> Char
int2let n = C.chr (C.ord 'a' + n)     

-- a-zなら、(mod n 26)個分シフトしたアルファベットを返す
shift                 :: Int -> Char -> Char
shift n c | C.isLower c = int2let ((let2int c + n) `mod` 26)
          | otherwise = c

encode      :: Int -> String -> String
encode n xs =  [ shift n x | x <- xs ]

table :: [Float]
table  = [8.2, 1.5, 2.8, 4.3, 12.7, 2.2, 2.0, 6.1, 7.0, 0.2, 0.8, 4.0, 2.4, 
          6.7, 7.5, 1.9,  0.1, 6.0, 6.3, 9.1, 2.8, 1.0, 2.4, 0.2, 2.0, 0.1]

percent     :: Int -> Int -> Float
percent n m  = (fromIntegral n / fromIntegral m ) * 100

count x xs = length [ i | i <- xs, x == i ]
lowers xs  = length [ c | c <- xs, 'a' <= c && c <= 'z' ]

freqs    :: String -> [Float]
freqs xs  = [percent (count x xs) n | x <- ['a' .. 'z' ] ]
            where n = lowers xs

-- 母集団の要素eとそれ以外の集団oが
-- どれくらい離れているかを計算する
chisqr      :: [Float] -> [Float] -> Float
chisqr os es = sum [((o - e) ^ 2) / e | (o, e) <- zip os es]

rotate     :: Int -> [a] -> [a]
rotate n xs = (drop n xs) ++ (take n xs)

-- encodeされたxsを(-factor)分encodeする -> decode
crack    :: String -> String
crack xs  = encode (-factor) xs
  where
    factor = head (positions (minimum chitab) chitab)
    chitab = [ chisqr (rotate n table') table | n <- [0..25]]
    table' = freqs xs   

すごい!すごい!すごい!

*Main> crack "kdvnhoo lv ixq"
"haskell is fun"

明日はバシッと練習問題!