LoginSignup
1
1

More than 3 years have passed since last update.

Programming in Haskell の練習問題の実装例 Part.1

Posted at

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))]
1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1