プログラミングHaskell 第7章 高階関数
前章に比べるとレベル上がった印象。
import Data.Char(chr, ord, isLower, isUpper) -- 7.1 基本概念 twice :: (a -> a) -> a -> a twice f x = f (f x) -- 7.2 リスト処理 map :: (a -> b) -> [a] -> [b] map f xs = [f x | x <- xs] map f [] = [] map f (x:xs) = f x : map f xs filter :: (a -> Bool) -> [a] -> [a] filter p xs = [x | x <- xs, p x] filter p [] = [] filter p (x:xs) | p x = x : filter p xs | otherwise = filter p xs -- 整数のリストから偶数を取り出し、二乗した値の合計をとる sumsqreven :: [Int] -> Int sumsqreven ns = sum (map (^2) (filter even ns)) -- 7.3 畳込 foldr sum = foldr (+) 0 product = foldr (*) 1 or = foldr (||) False and = foldr (&&) True foldr :: (a -> b -> b) -> b -> [a] -> b foldr f v [] = v foldr f v (x:xs) = f x (foldr f v xs) length :: [a] -> Int length [] = 0 length (_:xs) = 1 + length xs length = foldr (\_ n -> 1+n) 0 reverse :: [a] -> [a] reverse [] = [] reverse (x:xs) = reverse xs ++ [x] -- 新しい要素をリストの末尾に追加する snoc :: a -> [a] -> [a] snoc x xs = xs ++ [x] reverse [] = [] reverse (x:xs) = snoc x (reverse xs) reverse foldr snoc [] -- error -- (++ys) = foldr (:) ys -- 7.4 畳込関数 foldl sum = foldl (+) 0 product = foldl (*) 1 or = foldl (||) False and = foldl (&&) True length = foldl (\n _ -> n+1) 0 reverse = foldl (\xs x -> x:xs) [] -- error -- (xs++) = foldl (\ys y -> ys ++ [y]) xs foldl :: (a -> b -> a) -> a -> [b] -> a foldl f v [] = v foldl f v (x:xs) = foldl f (f v x) xs -- 7.5 関数合成演算子 (.) :: (b -> c) -> (a -> b) -> (a -> c) f.g = \x -> f (g x) odd n = not (even n) odd = not.even twice f x = f (f x) twice f = f.f sumsqreven ns = sum (map (^2) (filter even ns)) sumsqreven = sum.map (^2).filter even id :: a -> a id = \x -> x compose :: [a -> a] -> (a -> a) compose = foldr (.) id -- 7.6 文字列の変換器 type Bit = Int bin2int :: [Bit] -> Int -- bin2int bits = sum [w*b | (w,b) <- zip weights bits] -- where weights = iterate (*2) 1 bin2int = foldr (\x y -> x+2*y) 0 int2bin :: Int -> [Bit] int2bin 0 = [] int2bin n = n `mod` 2 : int2bin (n `div` 2) -- 二進表記を8ビットにする make8 :: [Bit] -> [Bit] make8 bits = take 8 (bits ++ repeat 0) encode :: String -> [Bit] encode = concat.map (make8.int2bin.ord) -- 8ビットの二進表記に分割する chop8 :: [Bit] -> [[Bit]] chop8 [] = [] chop8 bits = take 8 bits : chop8 (drop 8 bits) decode :: [Bit] -> String decode = map (chr.bin2int).chop8 transmit :: String -> String transmit = decode.channel.encode channel :: [Bit] -> [Bit] channel = id
exercise.
1.
リスト内包表記 [ f x | x <- xs, p x ] を
高階関数 map と filter を使って書き直すとどうなるか?
map f ( filter p xs )
2.
標準ライブラリでの定義を見ないで、 高階関数all, any, takeWhile, およびdropWhileを定義せよ。 myAll :: (a -> Bool) -> [a] -> Bool myAll p [] = True myAll p (x:xs) = (p x) && myAll p xs myAny :: (a -> Bool) -> [a] -> Bool myAny p [] = False myAny p (x:xs) = (p x) || myAny p xs myTakeWhile :: (a -> Bool) -> [a] -> [a] myTakeWhile p [] = [] myTakeWhile p (x:xs) | p x = x : myTakeWhile p xs | otherwise = [] myDropWhile :: (a -> Bool) -> [a] -> [a] myDropWhile p [] = [] myDropWhile p (x:xs) | p x = myDropWhile p xs | otherwise = x:xs
3.
関数foldrを用いて、関数map f と filter p を定義せよ。
ここでちょっと詰まりました。
map fは2つ定義が書いてあります。
まずはラムダ式の方を思いついたのですが、うーん?というのをTwitterでつぶやいたところ@osanai_先生に2つめのを教えてもらいました。
でもそれをfilterの方には全然いかせてません。
map :: (a -> b) -> [a] -> [b] map f = foldr (\x y -> f x : y) [] map f = foldr ((:).f) [] filter :: (a -> Bool) -> [a] -> [a] filter f = foldr (\x y -> if f x then x:y else y) []
4.
foldlを用いて、十進表記を整数に変換する関数
dec2int :: [Int] -> Int
を定義せよ。
dec2int :: [Int] -> Int dec2int = foldr (\x y -> x + 10*y) 0
5.
以下の定義が誤りである理由を説明せよ。
sumsqreven = compose [sum, map (^2), filter even]
compose :: [a -> a] -> (a -> a)
map(^2), filter evenは同じ型だが、sumのみ異なるため。
6.
標準ライブラリの定義を見ないで、以下の2つの高階関数を定義せよ。
・「引数に組をとる関数」を「カリー化された関数」へ変換する関数 curry
・「引数が2つのカリー化された関数」を「引数に組をとる関数」へ変換する関数 uncurry
ヒント:まず、関数の型を書きなさい。
ここでどはまりしました。
引数を2つとる関数を定義する場合、まだどうしても左辺にf x y = みたいに書いてしまいます。
mycurry :: ((a, b) -> c) -> (a -> b -> c) mycurry = (\a b c -> a (b,c)) myuncurry :: (a -> b -> c) -> ((a, b) -> c) myuncurry = (\a (b,c) -> a b c)
7.
高階関数 unfold は、リストを生成する簡単な再帰の様式を閉じ込める。
この関数は、以下のように定義できる。
unfold p h t x | p x = []
| otherwise = h x : unfold p h t (t x)
すなわち、関数 unfold p h t は、
述語pを引数に適用した結果Trueとなれば、空リストを返す。
そうでなければ、関数hを引数へ適用することで先頭の要素を作り、
残りのリストは自分自身を呼び出すことで生成して、全体として空でないリストを作る。
再帰する際には、関数tを引数に適用して、新たな引数を作る。
たとえば関数unfoldを用いると、関数int2binはもっと簡潔に書き直せる。int2bin = unfold (==0) (`mod`2) (`div`2)
関数 unfold を用いて関数 chop8, map f, iterate fを再定義せよ。
|
不完全です。というのもmapで適用できる型にEqクラスであるという制限がついてしまっています。
(==[])のせいなんですが、どうしたもんでしょうか・・・
unfold :: (a -> Bool) -> (a -> b) -> (a -> a) -> a -> [b] unfold p h t x | p x = [] | otherwise = h x : unfold p h t (t x) chop8_unfold :: [Bit] -> [[Bit]] chop8_unfold = unfold (==[]) (take 8) (drop 8) map_unfold :: Eq a => (a -> b) -> [a] -> [b] map_unfold f = unfold (==[]) (f.head) (tail)
8.
パリティービットを用いることで、
文字列を通信するプログラムを軽度の通信エラーを検出できるように変更せよ。
すなわち、8ビットの二進表記は、符号化の際にパリティービットを付加される。
パリティービットは、1の個数が奇数であれば1, そうでなければ0となる。
逆に複号の際は、9ビットの二進表記のパリティービットが正しいか検査する。
正しければパリティービットを捨て誤りであればパリティーエラーを報告する。
ヒント: ライブラリ関数 error :: String -> a は、
評価を強制終了し、与えられる文字列をエラーメッセージとして表示する。
check_parity中でerrorを使ってるんですが、
これcheck_parityの型を完全に無視してる気がするんだけど大丈夫なの?(´・ω・`)
それから、decode_parityの定義がたぶんなんかおかしいです。
ほんとはmap(chr.bin2int.check_parity).chop9と書きたかったのですが、
bin2int.check_parityのとこで怒られてしまいました。
(.)演算子がちゃんとわかってない。
-- パリティを先頭に付加 make9_parity :: [Bit] -> [Bit] make9_parity bits = length (filter odd bits) `mod` 2 : make8 bits encode_parity :: String -> [Bit] encode_parity = concat.map(make9_parity.int2bin.ord) -- まじめにやるならchop nを作ってchop8, chop9はそれを使う方がよい気がする chop9 :: [Bit] -> [[Bit]] chop9 [] = [] chop9 bits = take 9 bits : chop9 (drop 9 bits) check_parity :: [Bit] -> [Bit] check_parity (x:xs) | x == checked_parity = xs | otherwise = error "parity error" where checked_parity = length (filter odd xs) `mod` 2 decode_parity :: [Bit] -> String decode_parity = map(chr.bin2int).map(check_parity).chop9
9.
通信エラーの生じる通信路を用いて、
練習問題8で定義した文字列を通信するプログラムを試せ。
この通信路は最初のビットを落とすとする。
これは、関数tailをビットのリストに適用することで実現できる。
transmit_normal :: String -> String transmit_normal = decode_parity.channel.encode_parity transmit_broken :: String -> String transmit_broken = decode_parity.channel_broken.encode_parity channel_broken :: [Bit] -> [Bit] channel_broken = tail
Wolfram AlphaでFizzBuzz
数ヶ月前にこんなツイートをしました。
Wolfram AlphaでFizzBuzzできた! → Map[ReplaceAll[GCD[#,15],{15->"FizzBuzz",3->"Fizz",5->"Buzz",_->#}]&,Range[100]]
— りんごさん (@aomoriringo) 10月 11, 2011
今更ながら、ちょっと解説記事を書こうと思い立ったので書きます。
ブログ開設による推進力というやつです。
Map[ReplaceAll[GCD[#,15], {15 -> "FizzBuzz", 3 -> "Fizz", 5 -> "Buzz", _ -> #}]&, Range[100]]
上記のコードを実行すると以下のようになります。
解説(丁寧編)
では少しずつ分解していきましょう。
まずはMap, Rangeから。
Map: 第2引数に与えるリストの要素を1つずつ取り出して、第1引数の式を適用させる
Range: 1からnまでの整数のリストを返す
Map[#^2&, Range[10]]
Result: {1, 4, 9, 16, 25, 36, 49, 64, 81, 100}
このとき、#は取り出した要素を表します。
&は、#がある式の最後に付ける必要のある記号で、演算子とかではないです。
続いてGCDです。
GCD: 最大公約数を返す
これをMap, Rangeと組み合わせるとこうなります。
Map[ GCD[#, 15]&, Range[30] ]
Result: {1, 1, 3, 1, 5, 3, 1, 1, 3, 5, 1, 3, 1, 1, 15, 1, 1, 3, 1, 5, 3, 1, 1, 3, 5, 1, 3, 1, 1, 15}
これに対してあとはReplaceAllを適用して完成です。
ReplaceAll: リストに対して変換の規則を適用する
ReplaceAll[ {x, y, z, a}, {x -> m, y -> n} ]
Result: {m, n, z, a}
パターンマッチですね。
このReplaceAllを使って、先ほどのGCDの式に
・15だったら"FizzBuzz"に変換
・3だったら"Fizz"に変換
・5だったら"Buzz"に変換
・それ以外だったらそのままの数字
というのを書きます。
Map[ReplaceAll[GCD[#,15], {15 -> "FizzBuzz", 3 -> "Fizz", 5 -> "Buzz", _ -> #}]&, Range[100]]
できあがり。
駄文
ReplaceAllには「/.」、Mapには「/@」というシンタックスシュガーが用意されています。
(Wolfram Alphaに今回のFizzBuzzの式を入力すると、Inputとして「/.」「/@」という記号に変換されているのが確認できます。)
Mathematicaではもちろん常に使用できますが、Wolfram Alphaでは入力の複雑さによって読み取ってくれたりくれなかったりします。
Mathematica勉強会でも以前この話を出してみたら、
- 式が読み取れる場合と読み取れない場合の違いがよくわからない
- シンタックスシュガーを使っても使わなくても本質的には違いがないのに使えなくなる意味がわからない
などの話が現れ、最終的に
「Wolfram Alphaのパーサはクソ」
という結論になっていました。
プログラミングHaskell 6章
演習問題の6だけ「?」が残りました。foldrとか使うんだと思うんだけど・・・
どうやったら簡略化できるんだろ?
-- 6. 再帰関数 -- 6.1 基本概念 factorial :: Int->Int -- factorial n = product [1..n] factorial 0 = 1 factorial n = n * factorial (n-1) -- (*) :: Int -> Int -> Int -- m * 0 = 0 -- m * n = m + (m*(n-1)) -- 6.2 リストに対する再帰 -- product :: Num a => [a] -> a -- product [] = 1 -- product (n:ns) = n * product ns -- length :: [a] -> Int -- length [] = 0 -- length (_:xs) = 1 + length xs -- reverse :: [a] -> [a] -- reverse [] = [] -- reverse (x:xs) = reverse xs ++ [x] -- (++) :: [a] -> [a] -> [a] -- [] ++ ys = ys -- (x:xs) ++ ys = x:(xs ++ ys) insert :: Ord a => a -> [a] -> [a] insert x [] = [x] insert x (y:ys) | x <= y = x:y:ys | otherwise = y:insert x ys -- insertion sort isort :: Ord a => [a] -> [a] isort [] = [] isort (x:xs) = insert x (isort xs) -- 6.3 複数の引数 -- zip :: [a] -> [b] -> [(a,b)] -- zip [] _ = [] -- zip _ [] = [] -- zip (x:xs) (y:ys) = (x,y) : zip xs ys -- drop :: Int -> [a] -> [a] -- drop 0 xs = xs -- drop n [] = [] -- drop n (_:xs) = drop (n-1) xs -- 6.4 多重再帰 fibonacci :: Int -> Int fibonacci 0 = 0 fibonacci 1 = 1 fibonacci n = fibonacci (n-2) + fibonacci (n-1) -- quick sort qsort :: Ord a => [a] -> [a] qsort [] = [] qsort (x:xs) = qsort smaller ++ [x] ++ qsort larger where smaller = [ a | a<-xs, a<=x ] larger = [ b | b<-xs, b>x ] -- 6.5 相互再帰 -- even :: Int -> Bool -- even 0 = True -- even n = odd n-1 -- odd :: Int -> Bool -- odd 0 = False -- odd n = even n-1 -- リストから偶数の位置の要素を取り出す evens :: [a] -> [a] evens [] = [] evens (x:xs) = x:odds xs -- リストから奇数の位置の要素を取り出す odds :: [a] -> [a] odds [] = [] odds (_:xs) = evens xs
exercise
1.
乗算演算子 * の再帰を参考にして、負でない整数に対する累乗演算子 ^ を定義せよ。
また、その定義を使って、2^3を簡約せよ。
(^) = Int -> Int -> Int m ^ 0 = 1 m ^ n = m * (m ^ (n-1)) 2^3 = 2 * (2^2) = 2 * (2 * (2^1)) = 2 * (2 * (2 * (2^0))) = 2 * (2 * (2 * (1))) = 8
2.
この章で与えた定義を使って、length [1,2,3], drop 3 [1,2,3,4,5],
およびinit[1,2,3]を簡約せよ。
length [1,2,3] = 1 + length [2,3] = 1 + (1 + length [3]) = 1 + (1 + (1 + length []) = 1 + (1 + (1 + 0)) = 3 drop 3 [1,2,3,4,5] = drop 2 [2,3,4,5] = drop 1 [3,4,5] = drop 0 [4,5] = [4,5] init [1,2,3] = 1 : init [2,3] = 1 : (2 : init [3]) = 1 : (2 : []) = [1,2]
3.
標準ライブラリを見ないで、以下のライブラリ関数を再起を使って定義せよ。
and :: [Bool] -> Bool and [x] = x and (x:xs) = x && and xs concat :: [[a]] -> [a] concat [] = [] concat (x:xs) = x ++ concat xs replicate :: Int -> a -> [a] replicate 0 x = [] replicate n x = x : replicate2 (n-1) x (!!) :: [a] -> Int -> a (x:xs) !! 0 = x (x:xs) !! n = xs !! (n-1) elem :: Eq a => a -> [a] -> Bool elem a [] = False elem a (x:xs) = a==x || elem a xs
4.
関数 merge :: Ord a => [a] -> [a] -> [a]は、整列された2つのリストを2つとり、
1つの整列されたリストにして返す関数である。
関数 merge を再帰を用いて定義せよ。
ただし、関数 insertやisortなど、整列されたリストを処理する関数は利用してはならない。
merge :: Ord a => [a] -> [a] -> [a] merge [] [] = [] merge (x:xs) [] = x : merge xs [] merge [] (y:ys) = y : merge [] ys merge (x:xs) (y:ys) | x < y = x : merge xs (y:ys) | otherwise = y : merge (x:xs) ys
5.
関数 merge を使って、マージソートを実行する関数
msort :: Ord a => [a] -> [a]
を再帰を用いて定義せよ。
マージソートは、引数のリストを2つに分割し、それぞれを整列した後、再び1つに戻すことで、
整列を実現する。
ただし、空リストと要素が1つのリストは、すでに整列されていると考える。
ヒント: 最初に、リストを半分に分割する関数 halve:: [a] -> ([a],[a])を定義せよ。
生成された2つのリストの長さは、高々1しか違わない。
halve :: [a] -> ([a], [a]) halve xs = (take n xs, drop n xs) where n = (length xs) `div` 2 msort :: Ord a => [a] -> [a] msort [] = [] msort (x:[]) = [x] msort xs = merge (msort a) (msort b) where (a,b) = halve xs
6.
五段階の工程を使って、以下のライブラリ関数を定義せよ。
数値のリストに対し要素の和を計算する関数sum
リストの先頭からn個の要素を取り出す関数take
空でないリストの末尾の要素を取り出す関数last
-- sum/1. sum :: (Num a) => [a] -> a -- sum/2. sum [] = sum (x:xs) = -- sum/3. sum [] = 0 sum (x:xs) = -- sum/4. sum [] = 0 sum (x:xs) = x + sum xs -- sum/5. sum = foldl (+) 0 -- take/1. take :: Int -> [a] -> [a] -- take/2. take 0 (x:xs) = take n (x:xs) = -- take/3. take 0 (x:xs) = [] take n (x:xs) = -- take/4. take 0 (x:xs) = [] take n (x:xs) = x : take (n-1) xs -- take/5. -- 一般化・・・? -- last/1. last :: [a] -> a -- last/2. last (x:[]) = last (x:xs) = -- last/3. last (x:[]) = x last (x:xs) = -- last/4. last (x:[]) = x last (x:xs) = last xs -- last/5. -- ???
プログラミングHaskell 5章
リスト内包表記はMathematicaで慣れてるのでサクサク通過。
-- 5. リスト内包表記 import Data.Char(isLower, isUpper, ord, chr) -- 5.1 生成器 concat :: [[a]] -> [a] concat xss = [x | xs <- xss, x <- xs] firsts :: [(a,b)] -> [a] firsts ps = [x | (x,_)<-ps] -- in Prelude.hs -- length :: [a] -> Int -- length xs = sum [1| _ <- xs ] -- 5.2 ガード -- 正の整数に対し、すべての約数を計算する factors :: Int -> [Int] factors n = [ x | x <- [1..n], n`mod`x==0 ] -- 整数が素数か否かを判定する prime :: Int -> Bool prime n = factors n == [1,n] -- 与えられた上限までの素数全てを生成する primes :: Int -> [Int] primes n = [ x | x<-[2..n], prime x] find :: Eq a => a -> [(a,b)] -> [b] find k t = [ v | (k',v)<-t, k == k'] -- 5.3 関数zip -- リストから隣り合う要素を組にして、リストとして返す pairs :: [a] -> [(a,a)] pairs xs = zip xs (tail xs) -- 順序クラスに属する任意の型の要素を持つリストが、 -- 整列されているか調べる sorted :: Ord a => [a] -> Bool sorted xs = and [x<=y | (x,y)<- pairs xs] -- 目的とする値がリストのどの位置にあるかを調べて、 -- その位置すべてをリストとして返す positions :: Eq a => a->[a]->[Int] positions x xs = [ i | (x',i)<-zip xs[0..n], x==x'] where n = length xs - 1 -- 5.4 文字列の内包表記 -- 小文字の個数を数える lowers :: String -> Int lowers xs = length [ x | x<-xs, isLower x] -- 特定の文字の個数を数える count :: Char -> String -> Int count x xs = length [ x' | x' <- xs, x==x' ] -- 5.5 シーザー暗号 -- 小文字を0から25の整数に変換する let2int :: Char->Int let2int c = ord c - ord 'a' -- let2intの逆関数 int2let :: Int->Char int2let n = chr(ord 'a' + n) -- 小文字をシフト数だけずらす shift :: Int -> Char -> Char shift n 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 -- 任意の文字列に対して文字の出現頻度表を返す freqs :: String -> [Float] freqs xs= [percent (count x xs) n | x <- ['a'..'z']] where n = lowers xs -- カイ二乗検定 chisqr :: [Float] -> [Float] -> Float chisqr os es = sum [((o-e)^2) / e | (o,e)<-zip os es] -- リストの要素をnだけ左に回転させる rotate :: Int -> [a] -> [a] rotate n xs = drop n xs ++ take n xs 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
Exercise
1.
リスト内包表記を使って、
1から100までの二乗の和 1^2 + 2^2 + ... + 100^2 を計算する式を考えよ。
sum [x^2 | x<-[1..100]]
2.
関数lengthと同じように、ある要素のみからなるリストを生成するライブラリ関数
replicate :: Int -> a -> [a] をリスト内包表記を用いて定義せよ。
replicate :: Int -> a -> [a] replicate n a = [a | x<-[1..n]]
3.
x^2+y^2=z^2 を満たす正の整数をピタゴラス数と予備、3つ組(x,y,z)で表す。
ピタゴラス数のリストを生成する関数
pyths :: Int -> [(Int,Int,Int)]をリスト内包表記を使って定義せよ。
ただし、ピタゴラス数の要素は、与えられた上限以下であるとする。
pyths :: Int -> [(Int,Int,Int)] pyths n = [(x,y,z) | x<-[1..n], y<-[1..n], z<-[1..n], x^2+y^2==z^2]
4.
自分自身を除く約数の和が自分自身と等しいとき、その整数を完全数と呼ぶ。
与えられた上限までに含まれる完全数すべてを算出する関数
perfects :: Int -> [Int] をリスト内包表記と関数factorsを使って定義せよ。
perfects :: Int -> [Int] perfects n = [ x | x<-[1..n], x == (sum (factors x))-x]
5.
2つの生成器を持つリスト内包表記
[(x,y) | x<-[1,2,3], y<-[4,5,6]]
は、1つの生成器を持つリスト内包表記2つでも表現できることを示せ。
ヒント: 一方のリスト内包表記を他方への中に入れ、またライブラリ関数concatも使え。
Main.concat [[(x,y) | y<-[4..6]] | x<-[1..3]]
6.
関数positions を関数findを使って再定義せよ。
positions2 :: Eq a => a->[a]->[Int] positions2 x xs = find x [(a,b) | (a,b) <- zip xs [0..n]] where n = length xs - 1
7.
長さがnである整数のリストxsとysの内積は、対応する要素の積の和として計算できる。
関数chisqrと同様に、2つのリストから内積を計算する関数
scalarproduct::[Int] -> [Int] -> Int
をリスト内包表記を使って定義できることを示せ。
scalarproduct :: [Int] -> [Int] -> Int scalarproduct xs ys = sum [x*y | (x,y)<- zip xs ys]
8.
シーザー暗号のプログラムを大文字も扱えるように変更せよ。
uppers :: String -> Int uppers xs = length [ x | x<-xs, isUpper x] let2intUpper :: Char->Int let2intUpper c = ord c - ord 'A' int2letUpper :: Int->Char int2letUpper n = chr(ord 'A'+n) shift2 :: Int -> Char -> Char shift2 n c | isLower c = int2let((let2int c + n) `mod` 26) | isUpper c = int2letUpper((let2intUpper c + n) `mod` 26) | otherwise = c encode2 :: Int -> String -> String encode2 n xs = [shift2 n x | x<-xs] freqs2 :: String -> [Float] freqs2 xs= [percent (count x xs + count y xs) n | (x,y) <- zip ['a'..'z'] ['A'..'Z']] where n = lowers xs + uppers xs crack2 :: String -> String crack2 xs = encode2 (-factor) xs where factor = head (positions (minimum chitab) chitab) chitab = [chisqr (rotate n table') table | n<-[0..25] ] table' = freqs2 xs
問8の関数名がひどすぎてすいません・・・