「Ansi Common Lisp」9章練習問題 - Haskell版
1. 実数のリストを引数として、このリストが降順でない並びになっている場合に真を返す関数を定義せよ。
isDescendingOrder :: [Double] -> Bool isDescendingOrder [] = False isDescendingOrder (x:[]) = True isDescendingOrder (x:xs) = if x > (head xs) then isDescendingOrder xs else False isNotDescendingOrder :: [Double] -> Bool isNotDescendingOrder xs = if isDescendingOrder xs then False else True main = do print $ isNotDescendingOrder [0.3, 0.2, 0.1] print $ isNotDescendingOrder [0.1, 0.2, 0.3] print $ isNotDescendingOrder [0.3, 0.1, 0.2]
結果はこう。
False True True
2. 整数のセントを引数とし、その金額とするための4種類のコイン(25セント、10セント、5セント、1セント)の組み合わせ(コインの数は最小になるように)を示す4つの値を返す関数を定義せよ。
cents :: Int -> [Int] cents cent = [quarter, ten, five, one] where quarter = cent `div` 25 quarterMod = cent `mod` 25 ten = quarterMod `div` 10 tenMod = quarterMod `mod` 10 five = tenMod `div` 5 one = tenMod `mod` 5 main = do print $ cents 100 print $ cents 123 print $ cents 41
結果はこう。
[4,0,0,0] [4,2,0,3] [1,1,1,1]
3. はるかかなたの惑星に2種類の生物(wiggliesとwobblies)が住んでいるとする。wiggliesもwobbliesも同じ程度に歌うのがうまい。毎年、シンガーのベスト10を選ぶ大がかりなコンテストがもよおされる。以下の表は過去10年間の結果である。このようなコンテストをシミュレートするプログラムを書け。その結果から、審査委員は毎年、本当にベスト10を選んでいると言えるだろうか。
年 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
WIGGLIES | 6 | 5 | 6 | 4 | 5 | 5 | 4 | 5 | 6 | 5 |
WOBBLIES | 4 | 5 | 4 | 6 | 5 | 5 | 6 | 5 | 4 | 5 |
import List import System.Random -- n人分のスコアをリストにして返す getScores :: Int -> String -> IO [(String, Int)] getScores n name = do stdGen <- getStdGen return $ zip (replicate n name) (take n $ randomRs (1, 100 ) stdGen) -- タプルリストをスコアの降順にソートする sortScores :: [(String, Int)] -> [(String, Int)] sortScores xs = sortBy (\a b -> compare (snd b) (snd a)) xs main = do wiggliesScores <- getScores 100 "wigglies" wobbliesScores <- getScores 100 "wobblies" print $ take 10 $ sortScores $ wiggliesScores ++ wobbliesScores
サマリするところまで作ってませんが、まあ大体動きました。
乱数使うのにもモナドかよって感じですが、やっぱりモナドわからないなあ。
[("wigglies",99),("wobblies",99),("wigglies",97),("wigglies",97),("wobblies",97),("wobblies",97),("wigglies",95),("wigglies",95),("wigglies",95),("wobblies",95)]
6対4でwiggliesの勝ち!
4. 2次元平面における2つの線分の両端の点を示す8つの実数を引数として、線分が交わらないときはnilを返し、交わるときはx,yの座標の対を示す2値を返す関数を定義せよ。
-- 傾きを求める calcA :: Double -> Double -> Double -> Double -> Double calcA x1 y1 x2 y2 = (y2 - y1) / (x2 - x1) -- 切片を求める calcB :: Double -> Double -> Double -> Double calcB x1 y1 a = y1 - (a * x1) -- 傾きと切片を求めてタプルとして返す calcAB :: Double -> Double -> Double -> Double -> (Double, Double) calcAB x1 y1 x2 y2 = (a,b) where a = calcA x1 y1 x2 y2 b = calcB x1 y1 a -- 2つの線分の交点を求める calcCrossPoint :: Double -> Double -> Double -> Double -> Double -> Double -> Do uble -> Double -> (Double, Double) calcCrossPoint x1 y1 x2 y2 x3 y3 x4 y4 = if (fst ab1) == (fst ab2) then (-1, -1) else calcCrossPointFromAB ab1 ab2 where ab1 = calcAB x1 y1 x2 y2 ab2 = calcAB x3 y3 x4 y4 calcCrossPointFromAB :: (Double, Double) -> (Double, Double) -> (Double, Double) calcCrossPointFromAB ab1 ab2 = (x, y) where x = ((snd ab2) - (snd ab1)) / ((fst ab1) - (fst ab2)) y = ((fst ab1) * x) + (snd ab1) main = do print $ calcCrossPoint 2 1 6 6 2 6 6 1 print $ calcCrossPoint 2 1 4 2 2 4 4 5 print $ calcCrossPoint 2 6 6 1 2 (-4) 6 (-1)
結果はこう。
(4.0,3.5) (-1.0,-1.0) (7.0,-0.25)
うーん、相変わらずnil的なものを返したいときにどうすればいいのかわからない。。
Maybeとか使うのかしら。