ITコンサルの日常

ITコンサル会社に勤務する普通のITエンジニアの日常です。

「ANSI Common Lisp」4章練習問題 - Haskell版

1. 正方の配列(大きさが(n n)の配列)を引数としてそれを90度、時計回りに回転させる関数を定義せよ。

Lispの時に手抜きした分、めちゃくちゃ時間かかりました。。

-- 数列を右回りに90度回転
quarterTurn :: [[a]] -> [[a]]
quarterTurn det = foldl makeDet [] det

-- 受け取ったリストを右回りに90度回転
rotate :: [a] -> [[a]]
rotate xs = map (\n -> [n]) xs

-- 受け取ったリストを回転させた数列に追加
makeDet :: [[a]] -> [a] -> [[a]]
makeDet [] ys = rotate ys
makeDet xs ys = map (\t -> fst t : snd t) $ myzip ys xs

-- リストとリストのリストを受け取ってタプルに変換
myzip :: [a] -> [[b]] -> [(a, [b])]
myzip [] [] = []
myzip (x:xs) (y:ys) = (x, y) : myzip xs ys

-- テストドライバ
main = do print $ quarterTurn ["ab","cd"]
          print $ quarterTurn ["abc","def"]

結果はこう。

["ca","db"]
["da","eb","fc"]

2. reduceを使って以下のものを定義せよ

(a) copy-list
myCopyList xs = foldr (:) [] xs

main = do print [1..3]
          print $ myCopyList [1..3]

結果はこう。

[1,2,3]
[1,2,3]
(b) reverse(リストが対象)
myCopyList xs = foldl (\a b -> b : a) [] xs

main = do print $ reverse [1..3]
          print $ myCopyList [1..3]

結果はこう。

[3,2,1]
[3,2,1]

3. それぞれのノードがデータをもち、3つまでの子がとれるようなツリーを表現するストラクチャを定義した上で、以下の関数を定義せよ。

とりあえずストラクチャを定義。

-- 3つのノードを持つ木構造を定義
data MyNode d l c r = Empty | MyNode d (MyNode d l c r) (MyNode d l c r) (MyNode
 d l c r)

-- データを定義
-- 1 --- 2
--    |
--    -- 3 --- 5
--    |     |
--    |     -- 6
--    |     |
--    |     -- 7
--    | 
--    -- 4
childNode7 = MyNode 7 Empty Empty Empty
childNode6 = MyNode 6 Empty Empty Empty
childNode5 = MyNode 5 Empty Empty Empty
childNode4 = MyNode 4 Empty Empty Empty
childNode3 = MyNode 3 childNode5 childNode6 childNode7
childNode2 = MyNode 2 Empty Empty Empty
rootNode = MyNode 1 childNode2 childNode3 childNode4

main = print $ showMyNode rootNode

-- MyNodeを表示する関数
showMyNode :: (Show d) => (MyNode d l c r) -> String
showMyNode Empty = "Empty"
showMyNode (MyNode d l c r) = "(" ++ (show d) ++ "," ++ (showMyNode l) ++ "," ++
 (showMyNode c) ++ "," ++ (showMyNode r) ++ ")"

-- MyNodeから左ノードを取り出す
leftNode :: (MyNode d l c r) -> (MyNode d l c r)
leftNode (MyNode d l c r) = l

-- MyNodeから中央ノードを取り出す
centerNode :: (MyNode d l c r) -> (MyNode d l c r)
centerNode (MyNode d l c r) = c

-- MyNodeから右ノードを取り出す
rightNode :: (MyNode d l c r) -> (MyNode d l c r)
rightNode (MyNode d l c r) = r

結果はこう。

"(1,(2,Empty,Empty,Empty),(3,(5,Empty,Empty,Empty),(6,Empty,Empty,Empty),(7,Empty,Empty,Empty)),(4,Empty,Empty,Empty))"

見づらいがまあ出来てそう。
Haskellのデータ定義とかすっ飛ばしたせいか、さっぱり理解してない。
こんなんでいいのだろうか。。

(a) このようなツリーをコピーする関数(コピーのノードがオリジナルのノードとeqlにならないように)
-- MyNodeをコピーする
copyMyNode :: (MyNode d l c r) -> (MyNode d l c r)
copyMyNode Empty = Empty
copyMyNode (MyNode d l c r) = MyNode d (copyMyNode l) (copyMyNode c) (copyMyNode r)

main = do print $ showMyNode rootNode
          print $ showMyNode $ copyMyNode rootNode

結果はこう。

"(1,(2,Empty,Empty,Empty),(3,(5,Empty,Empty,Empty),(6,Empty,Empty,Empty),(7,Empty,Empty,Empty)),(4,Empty,Empty,Empty))"
"(1,(2,Empty,Empty,Empty),(3,(5,Empty,Empty,Empty),(6,Empty,Empty,Empty),(7,Empty,Empty,Empty)),(4,Empty,Empty,Empty))"
(b) オブジェクトとこのようなツリーを引数として、オブジェクトがツリーのノードのデータフィールドにeqlであるときに真を返す関数
-- MyNodeから値を検索する
findMyNode :: (Show a, Show d) => a -> (MyNode d l c r) -> Bool
findMyNode _ Empty = False
findMyNode a (MyNode d l c r) = if ((show a) == (show d)) then True
                                else (findMyNode a l) || (findMyNode a c) || (fi
ndMyNode a r)

main = do print $ showMyNode rootNode
          print $ showMyNode $ copyMyNode rootNode
          print $ findMyNode 5 rootNode
          print $ findMyNode 8 rootNode

大分ムリヤリですが、結果はこう。

True
False

MyNodeのdを型クラスEqに縛れれば良いのかと思うのですが、そういうことって出来ないんですかね。
MyNode自体をEqに縛ることは、もちろんできますが。