ITコンサルの日常

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

「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とか使うのかしら。