Programming in Haskell という書籍で Haskell の勉強をしています(日本語版はこちら).
練習問題がついているので自分なりに実装してみました.
答え合わせもかねているので,間違っている点や改善点があったら教えていただけるとうれしいです.
関数名があらかじめ Haskell で定義されているものと被る場合にはなるべく '
などの記号を付け足すようにしています
1. Introduction
Exercise 3
mul' :: Num a => [a] -> a
mul' [] = 1
mul' (x : xs) = x * mul' xs
Exercise 4
rqsort :: Ord a => [a] -> [a]
rqsort [] = []
rqsort (x : xs) = rqsort larger ++ [x] ++ rqsort smaller
where
smaller = [a | a <- xs, a < x]
larger = [b | b <- xs, b >= x]
2. First steps
Exercise 4
last' :: [a] -> a
last' xs = xs !! (length xs - 1)
last'' :: [a] -> a
last'' [x] = x
last'' (x : xs) = last'' xs
Exercise 5
init' :: [a] -> [a]
init' xs = take (length xs - 1) xs
init'' :: [a] -> [a]
init'' [x] = []
init'' (x : xs) = x : init'' xs
3. Types and classes
Exercise 2
bools :: [Bool]
bools = [True, False, True, False]
nums :: [[Int]]
nums = [[1, 2, 3], [4, 5], [6, 7, 8, 9]]
add :: Int -> Int -> Int -> Int
add x y z = x + y + z
copy :: a -> (a, a)
copy a = (a, a)
apply :: (a -> b) -> a -> b
apply f = f
4. Defining functions
Exercise 1
halve xs = splitAt n xs
where
n = length xs `div` 2
Exercise 2
third :: [a] -> a
third xs = head (tail (tail xs))
thirdB :: [a] -> a
thirdB xs = xs !! 2
thirdC :: [a] -> a
thirdC (_ : _ : x : _) = x
Exercise 3
safetail :: [a] -> [a]
safetail xs = if null xs then tail xs else xs
safetailB :: [a] -> [a]
safetailB xs
| null xs = tail xs
| otherwise = xs
safetailC :: [a] -> [a]
safetailC (_ : xs) = xs
safetailC [] = []
Exercise 4
(||) :: Bool -> Bool -> Bool
False || False = False
_ || _ = Tru
Exercise 5
(&&) :: Bool -> Bool -> Bool
(&&) x y =
if x
then y
else False
Exercise 6
(&&) :: Bool -> Bool -> Bool
(&&) x b =
if x
then b
else False
Exercise 7
mult :: Integer -> Integer -> Integer -> Integer
mult = \x -> (\y -> (\z -> x * y * z))
Exercise 8
luhnDouble x =
if x * 2 > 9
then x * 2 - 9
else x * 2
Exercise 9
luhn :: Int -> Int -> Int -> Int -> Bool
luhn w x y z = (luhnDouble w + x + luhnDouble y + z) `mod` 10 == 0
5. List comprehensions
Exercise 1
sum [x ^ 2 | x <- [1 .. 100]]
Exercise 2
grid :: Int -> Int -> [(Int, Int)]
grid x y = [(x', y') | x' <- [0 .. x], y' <- [0 .. y]]
Exercise 3
square :: Int -> [(Int, Int)]
square n = [(x, y) | (x, y) <- grid n n, x /= y]
Exercise 4
replicate' :: Int -> a -> [a]
replicate' n a = [a | _ <- [0 .. n - 1]]
Exercise 5
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]
Exercise 6
factors :: Int -> [Int]
factors n = [x | x <- [1 .. n], n `mod` x == 0]
perfect :: Int -> [Int]
perfect n = [x | x <- [1 .. n], sum (init (factors x)) == x]
Exercise 7
concat [[(x, y) | x <- [1, 2]] | y <- [3, 4]]
Exercise 8
positions :: Eq a => a -> [a] -> [Int]
positions x xs = find x (zip xs [0 ..])
Exercise 9
scalarproduct :: [Int] -> [Int] -> Int
scalarproduct xs ys = sum [x * y | (x, y) <- zip xs ys]
Exercise 10
import Data.Char
positions :: Eq a => a -> [a] -> [Int]
positions x xs = [i | (x', i) <- zip xs [0 ..], x == x']
lowers :: [Char] -> Int
lowers xs = length [x | x <- xs, isAsciiLower x]
uppers :: [Char] -> Int
uppers xs = length [x | x <- xs, isAsciiUpper x]
count :: Eq a => a -> [a] -> Int
count x xs = length [x' | x' <- xs, x == x']
lower2int :: Char -> Int
lower2int c = ord c - ord 'a'
int2lower :: Int -> Char
int2lower n = chr (ord 'a' + n)
upper2int :: Char -> Int
upper2int c = ord c - ord 'A'
int2upper :: Int -> Char
int2upper n = chr (ord 'A' + n)
shift :: Int -> Char -> Char
shift n c
| isLower c = int2lower ((lower2int c + n) `mod` 26)
| isUpper c = int2upper ((upper2int c + n) `mod` 26)
| otherwise = c
encode :: Int -> [Char] -> [Char]
encode n xs = [shift n x | x <- xs]
table :: [Float]
table = [8.1, 1.5, 2.8, 4.2, 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.0, 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 + count y xs) n | (x, y) <- zip ['a' .. 'z'] ['A' .. 'Z']] where n = lowers xs
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
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 = do
print (crack "Kdvnhoo lv Ixq") --"Haskell is Fun"
print (crack "Vscd Mywzboroxcsyxc kbo Ecopev") -- "List Comprehensions are Useful"
6 Recursive functions
Exercise 1
fac :: Int -> Int
fac n
| n <= 0 = 1
| otherwise = n * fac (n - 1)
Exercise 2
sumdown' :: Int -> Int
sumdown' n
| n <= 0 = 0
| otherwise = n + sumdown' (n - 1)
Exercise 3
(^+) :: Int -> Int -> Int
x ^+ 0 = 1
x ^+ n = x * x ^+ (n - 1)
Exercise 4
euclid' :: Int -> Int -> Int
euclid' a 0 = a
euclid' a b = euclid' b (a `mod` b)
Exercise 6
and' :: [Bool] -> Bool
and' [True] = True
and' (False : _) = False
and' (True : xs) = and' xs
concat' :: [[a]] -> [a]
concat' = foldr (++) []
replicate' :: Int -> a -> [a]
replicate' 0 _ = []
replicate' n x = x : replicate' (n - 1) x
(!!+) :: [a] -> Int -> a
(x : xs) !!+ 0 = x
(x : xs) !!+ n = xs !!+ (n - 1)
elem' :: Eq a => a -> [a] -> Bool
elem' v [] = False
elem' v (x : xs)
| v == x = True
| otherwise = elem' v xs
Exercise 7
merge :: Ord a => [a] -> [a] -> [a]
merge xs [] = xs
merge [] ys = ys
merge (x : xs) (y : ys)
| x <= y = x : merge xs (y : ys)
| otherwise = y : merge (x : xs) ys
Exercise 8
halve :: [a] -> ([a], [a])
halve xs = splitAt n xs
where
n = length xs `div` 2
msort :: Ord a => [a] -> [a]
msort [] = []
msort [x] = [x]
msort xs = merge (msort xs') (msort ys')
where
(xs', ys') = halve xs
Exercise 9
sum' :: Num a => [a] -> a
sum' = foldr (+) 0
take' :: Int -> [a] -> a
take' 0 (x : _) = x
take' n (x : xs) = take' (n - 1) xs
last' :: [a] -> a
last' [x] = x
last' (x : xs) = last xs
7. Higher-order functions
Exercise 1
listCmp :: (a -> b) -> (b -> Bool) -> [a] -> [b]
listCmp f p = filter p . map f
Exercise 2
all' :: (a -> Bool) -> [a] -> Bool
all' p = and . map p
any' :: (a -> Bool) -> [a] -> Bool
any' p = or . map p
takeWhile' :: (a -> Bool) -> [a] -> [a]
takeWhile' p (x : xs) = if p x then x : takeWhile' p xs else []
dropWhile' :: (a -> Bool) -> [a] -> [a]
dropWhile' p (x : xs) = if p x then dropWhile' p xs else x : xs
Exercise 3
map' :: (a -> b) -> [a] -> [b]
map' p = foldr (\x xs -> p x : xs) []
filter' :: (a -> Bool) -> [a] -> [a]
filter' p = foldr (\x xs -> if p x then x : xs else xs) []
Exercise 4
dec2int :: [Int] -> Int
dec2int = foldl (\v x -> 10 * v + x) 0
Exercise 5
curry' :: ((a, b) -> c) -> a -> b -> c
curry' f x y = f (x, y)
uncurry' :: (a -> b -> c) -> ((a, b) -> c)
uncurry' f (x, y) = f x y
Exercise 6
type Bit = Int
unfold :: (t -> Bool) -> (t -> a) -> (t -> t) -> t -> [a]
unfold p h t x
| p x = []
| otherwise = h x : unfold p h t (t x)
int2bin :: Int -> [Bit]
int2bin = unfold (== 0) (`mod` 2) (`div` 2)
chop8 :: [Bit] -> [[Bit]]
chop8 = unfold null (take 8) (drop 8)
map' :: (a -> b) -> [a] -> [b]
map' f = unfold null (f . head) tail
iterate' :: (a -> a) -> a -> [a]
iterate' f = unfold (const False) id f
Exercise 7-10
import Data.Char
type Bit = Int
bin2int :: [Bit] -> Int
bin2int = foldr (\x y -> x + 2 * y) 0
int2bin :: Int -> [Bit]
int2bin 0 = []
int2bin n = n `mod` 2 : int2bin (n `div` 2)
make8 :: [Bit] -> [Bit]
make8 bits = take 8 (bits ++ repeat 0)
addParity :: [Bit] -> [Bit]
addParity bits = if odd (sum bits) then bits ++ [1] else bits ++ [0]
encode :: String -> [Bit]
encode = concat . map (addParity . make8 . int2bin . ord)
chop9 :: [Bit] -> [[Bit]]
chop9 [] = []
chop9 bits = take 9 bits : chop9 (drop 9 bits)
parityCheck :: [Bit] -> [Bit]
parityCheck bits = if even (sum bits) then init bits else error "Incorrect!"
decode :: [Bit] -> String
decode = map (chr . bin2int . parityCheck) . chop9
channel :: [Bit] -> [Bit]
channel = id
transmit :: String -> String
transmit = decode . channel . encode
altMap :: (a -> b) -> (a -> b) -> [a] -> [b]
altMap f g [] = []
altMap f g (x : xs) = f x : altMap g f xs
luhnDouble :: Int -> Int
luhnDouble x
| x * 2 > 9 = x * 2 - 9
| otherwise = x * 2
luhn :: [Int] -> Bool
luhn xs = sum (altMap id luhnDouble xs) `mod` 10 == 0
main = do
print (addParity [1, 0, 0, 0, 0, 0, 0, 0]) -- [1,0,0,0,0,0,0,0,1]
print (addParity [1, 1, 0, 0, 0, 0, 0, 0]) -- [1,1,0,0,0,0,0,0,0]
print (encode "abc") -- [1,0,0,0,0,1,1,0,0,1,0,0,0,1,1,0,1,1,0,0,0,1,1,0]
print (decode [1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0]) -- "abc"
-- print (decode (tail [1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0])) -- "Incorrect!"
print (altMap (+ 10) (+ 100) [0, 1, 2, 3, 4]) -- [10,101,12,103,14]
print (luhn [7, 9, 9, 2, 7, 3, 9, 8, 7, 1, 3]) -- True
8. Declaring types and classes
Exercise 1
data Nat = Zero | Succ Nat
nat2int :: Nat -> Int
nat2int Zero = 0
nat2int (Succ n) = 1 + nat2int n
int2nat :: Int -> Nat
int2nat 0 = Zero
int2nat n = Succ (int2nat (n - 1))
add :: Nat -> Nat -> Nat
add Zero n = n
add (Succ m) n = Succ (add m n)
mult :: Nat -> Nat -> Nat
mult Zero n = Zero
mult (Succ m) n = add (mult m n) n
main = do
print (nat2int (mult (Succ (Succ (Succ Zero))) (Succ (Succ Zero)))) -- 6
Exercise 2
data Tree a = Leaf a | Node (Tree a) a (Tree a)
occurs :: Ord a => a -> Tree a -> Bool
occurs x (Leaf y) = x == y
occurs x (Node l y r) = case compare x y of
EQ -> True
LT -> occurs x l
GT -> occurs x r
t :: Tree Int
t = Node (Node (Leaf 1) 3 (Leaf 4)) 5 (Node (Leaf 6) 7 (Leaf 9))
main = do
print (occurs 5 t) -- True
print (occurs 8 t) -- False
Exercise 3
balanced
ってこれでいいんだろうか
data Tree a = Leaf a | Node (Tree a) (Tree a)
cnt :: Tree a -> Int
cnt (Leaf x) = 1
cnt (Node x y) = cnt x + cnt y
balanced :: Tree a -> Bool
balanced (Leaf x) = True
balanced (Node x y) = abs (cnt x - cnt y) <= 1
t1 :: Tree Int
t1 = Node (Node (Leaf 1) (Leaf 2)) (Node (Leaf 3) (Node (Leaf 4) (Leaf 5)))
t2 :: Tree Int
t2 = Node (Node (Leaf 1) (Leaf 2)) (Node (Leaf 3) (Node (Leaf 4) (Node (Leaf 5) (Leaf 6))))
main = do
print (balanced t1) -- True
print (balanced t2) -- False
Exercise 4
data Tree a = Leaf a | Node (Tree a) (Tree a)
deriving (Show)
balance :: [a] -> Tree a
balance [x] = Leaf x
balance xs = Node (balance xs') (balance ys')
where
(xs', ys') = splitAt (length xs `div` 2) xs
main = do
print (balance [1, 2, 3, 4, 5, 6, 7, 8, 9])
{-
Node
(Node
(Node
(Leaf 1) (Leaf 2))
(Node
(Leaf 3) (Leaf 4)))
(Node
(Node
(Leaf 5) (Leaf 6))
(Node
(Leaf 7)
(Node
(Leaf 8) (Leaf 9))))
-}
Exercise 5-6
data Expr = Val Int | Add Expr Expr
folde :: (Int -> a) -> (a -> a -> a) -> Expr -> a
folde f g (Val x) = f x
folde f g (Add x y) = g (folde f g x) (folde f g y)
eval :: Expr -> Int
eval = folde id (+)
size :: Expr -> Int
size = folde (const 1) (+)
main = do
let e = Add (Add (Val 1) (Val 2)) (Add (Add (Val 3) (Val 4)) (Val 5))
print (eval e) -- 15
print (size e) -- 5
Exercise 7
instance Eq a => Eq (Maybe a) where
Just x == Just y = x == y
Nothing == Nothing = True
_ == _ = False
x /= y = not (x == y)
instance Eq a => Eq [a] where
x == y = (length x == length y) && all [x' == y' | (x', y') <- zip x y]
x /= y = not (x == y)
Exercise 8
data Prop
= Const Bool
| Var Char
| Not Prop
| And Prop Prop
| Imply Prop Prop
| Or Prop Prop
| Iff Prop Prop
type Assoc k v = [(k, v)]
type Subst = Assoc Char Bool
find :: Eq k => k -> Assoc k v -> v
find k t = head [v | (k', v) <- t, k == k']
eval :: Subst -> Prop -> Bool
eval _ (Const b) = b
eval s (Var x) = find x s
eval s (Not p) = not (eval s p)
eval s (And p q) = eval s p && eval s q
eval s (Imply p q) = eval s p <= eval s q
eval s (Or p q) = eval s p || eval s q
eval s (Iff p q) = eval s (Imply p q) && eval s (Imply q p)
vars :: Prop -> [Char]
vars (Const _) = []
vars (Var x) = [x]
vars (Not p) = vars p
vars (And p q) = vars p ++ vars q
vars (Imply p q) = vars p ++ vars q
vars (Or p q) = vars p ++ vars q
vars (Iff p q) = vars p ++ vars q
bools :: Int -> [[Bool]]
bools 0 = [[]]
bools n = map (False :) bss ++ map (True :) bss
where
bss = bools (n - 1)
rmdups :: Eq a => [a] -> [a]
rmdups [] = []
rmdups (x : xs) = x : rmdups (filter (/= x) xs)
substs :: Prop -> [Subst]
substs p = map (zip vs) (bools (length vs))
where
vs = rmdups (vars p)
isTaut :: Prop -> Bool
isTaut p = and [eval s p | s <- substs p]
p1 :: Prop
p1 = Iff (Imply (Imply (Var 'A') (Var 'B')) (Var 'C')) (Or (And (Var 'A') (Not (Var 'B'))) (Var 'C'))
p2 :: Prop
p2 = Or (Imply (Var 'A') (Not (Var 'B'))) (And (Var 'C') (Var 'D'))
main = do
print (isTaut p1) -- True
print (isTaut p2) -- False
Exercise 9
data Expr = Val Int | Add Expr Expr | Mul Expr Expr
type Cont = [Op]
data Op = EVAL_ADD Expr | EVAL_MUL Expr | ADD Int | MUL Int
eval :: Expr -> Cont -> Int
eval (Val n) c = exec c n
eval (Add x y) c = eval x (EVAL_ADD y : c)
eval (Mul x y) c = eval x (EVAL_MUL y : c)
exec :: Cont -> Int -> Int
exec [] n = n
exec (EVAL_ADD y : c) n = eval y (ADD n : c)
exec (EVAL_MUL y : c) n = eval y (MUL n : c)
exec (ADD n : c) m = exec c (n + m)
exec (MUL n : c) m = exec c (n * m)
value :: Expr -> Int
value e = eval e []
main = do
print (value (Add (Add (Val 2) (Val 3)) (Val 4))) -- 9
print (value (Mul (Add (Mul (Val 2) (Val 3)) (Val 4)) (Mul (Add (Val 2) (Val 3)) (Val 4)))) -- 200
9. The countdown problem
Exercise 1
choices :: [a] -> [[a]]
choices xs = [ys | sub <- subs xs, ys <- perms sub]
Exercise 2
isChoice :: Eq a => [a] -> [a] -> Bool
isChoice [] [] = True
isChoice [] _ = False
isChoice _ [] = False
isChoice (x : xs) ys = isChoice xs (rm x ys)
Exercise 4
...
es :: [Expr]
es = [e | ns <- choices [1, 3, 7, 10, 25, 50], e <- exprs ns]
main = do
print (length es) -- 33665406
print (length [e | e <- es, not (null (eval e))]) -- 4672540
Exercise 5
valid :: Op -> Int -> Int -> Bool
valid Add _ _ = True
valid Sub _ _ = True
valid Mul _ _ = True
valid Div x y = y /= 0 && x `mod` y == 0
...
es :: [Expr]
es = [e | ns <- choices [1, 3, 7, 10, 25, 50], e <- exprs ns]
main = do
print (length es) -- 33665406
print (length [e | e <- es, not (null (eval e))]) -- 10839369
Exercise 6
c については "a suitable measure of simplicity" が具体的に何なのかわからなかったので,とりあえず ()
の深さが浅い順にソートしています.
import Data.List
data Op = Add | Sub | Mul | Div | Exp
instance Show Op where
show Add = "+"
show Sub = "-"
show Mul = "*"
show Div = "/"
show Exp = "^"
valid :: Op -> Int -> Int -> Bool
valid Add x y = x <= y
valid Sub x y = x > y
valid Mul x y = x /= 1 && y /= 1 && x <= y
valid Div x y = y /= 0 && y /= 1 && x `mod` y == 0
valid Exp x y = x /= 0 && x /= 1 && y > 1
apply :: Op -> Int -> Int -> Int
apply Add x y = x + y
apply Sub x y = x - y
apply Mul x y = x * y
apply Div x y = x `div` y
apply Exp x y = x ^ y
data Expr = Val Int | App Op Expr Expr
instance Show Expr where
show (Val n) = show n
show (App o l r) = brak l ++ show o ++ brak r
where
brak (Val n) = show n
brak e = "(" ++ show e ++ ")"
values :: Expr -> [Int]
values (Val n) = [n]
values (App _ l r) = values l ++ values r
eval :: Expr -> [Int]
eval (Val n) = [n | n > 0]
eval (App o l r) = [apply o x y | x <- eval l, y <- eval r, valid o x y]
subs :: [a] -> [[a]]
subs [] = [[]]
subs (x : xs) = yss ++ map (x :) yss
where
yss = subs xs
interleave :: a -> [a] -> [[a]]
interleave x [] = [[x]]
interleave x (y : ys) = (x : y : ys) : map (y :) (interleave x ys)
perms :: [a] -> [[a]]
perms [] = [[]]
perms (x : xs) = concat (map (interleave x) (perms xs))
choices :: [a] -> [[a]]
choices = concat . map perms . subs
split :: [a] -> [([a], [a])]
split [] = []
split [_] = []
split (x : xs) = ([x], xs) : [(x : ls, rs) | (ls, rs) <- split xs]
ops :: [Op]
ops = [Add, Sub, Mul, Div, Exp]
type Result = (Expr, Int)
combine' :: Result -> Result -> [Result]
combine' (l, x) (r, y) = [(App o l r, apply o x y) | o <- ops, valid o x y]
results :: [Int] -> [Result]
results [] = []
results [n] = [(Val n, n) | n > 0]
results ns = [res | (ls, rs) <- split ns, lx <- results ls, ry <- results rs, res <- combine' lx ry]
depth :: Expr -> Int
depth (Val n) = 1
depth (App o l r) = 1 + max (depth l) (depth r)
solutions'' :: [Int] -> Int -> [Expr]
solutions'' ns n = if minDiff == 0 then [e | (e, d) <- sols''] else [fst (head sols'')]
where
sols = [(e, abs (m - n)) | ns' <- choices ns, (e, m) <- results ns']
minDiff = minimum (map snd sols)
sols' = filter ((== minDiff) . snd) sols
sols'' = sortOn (depth . fst) sols'
main = do
print (solutions'' [1, 3, 7, 10, 25, 50] 765) -- [(25-10)*(1+50),(25-(3+7))*(1+50),((25-3)-7)*(1+50),((25-7)-3)*(1+50), ..., (50*(25+((3-1)^7)))/10]
print (solutions'' [1, 3, 7, 10, 25, 50] 831) -- [7+((1+10)*(25+50))]