ITコンサルの日常

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

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

Haskellに置き換えられそうなやつだけやってみる。

4.2つの数を引数として、大きい方を返す関数を定義せよ。

先日のエントリで、

大きい方って、同じ場合はどうするんでしょうね。
とりあえずnilを返すようにしてみました。

って書いたけど、Haskellにはnilが無いのか?
なんかそんな基本的なことも分かってなかったかと思うとガッカリだな。。


とりあえずHaskell版では=は2番目の引数が返ることにしました。

main = do print $ larger 1 2
          print $ larger 4 3
          print $ larger 6 6

larger::Int -> Int -> Int
larger a b = if a > b then a else b

結果はこう。

2
4
6

5.以下の関数は何をするものであろうか。

(a)

(defun enigma (x)
  (and (not (null x))
     (or (null (car x))
         (enigma (cdr x)))))
(b)

(defun mystery (x y)
  (if (null y)
    nil
    (if (eql (car y) x)
      0
      (let ((z (mystery x (cdr y))))
        (and z (+ z 1))))))

Haskellで再実装してみる。

-- 何もしないらしい関数enigma
enigma::[a] -> [a]
enigma [] = []
enigma (x:xs) = enigma xs

-- リスト中の要素位置を返す関数mystery
mystery::(Eq a) => a -> [a] -> Int
mystery _ [] = 0
mystery x (y:ys) = if (x == y)
                     then 0
                     else (1 + (mystery x ys))

main = do print $ enigma ([] :: [Int])
          print $ enigma [1..3]
          print $ mystery 1 []
          print $ mystery 1 [1..3]
          print $ mystery 2 [1..3]
          print $ mystery 3 [1..3]
          print $ mystery 9 [1..3]

結果はこう。

[]
[]
0
0
1
2
3

mysteryで見つからない場合の戻りはリスト数としました。
なんかやっぱりnilって概念がないと不便だぞ。

7.本章で導入したオペレータだけを使い、1つのリストを引数とし、そのリストの要素にリストが含まれる場合に真とする関数を定義せよ。

これをHaskellで定義するのは無理?

[1,[2,3],4]

みたいなリストを定義できるかと思ってたけど、無理らしい。
これをやるなら、

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

って書けってことらしい。

8.以下のような関数を反復と再帰の2通りの方法で定義せよ。

(a)正の整数を引数とし、その数のドット(ピリオド)を表示する。
printPeriodLoop::Int -> String
printPeriodLoop n = concatMap (\n -> ".") [1..n]

printPeriodRecursive::Int -> String
printPeriodRecursive 0 = ""
printPeriodRecursive n = "." ++ printPeriodRecursive(n-1)

main = do print $ printPeriodLoop 5
          print $ printPeriodRecursive 6

結果はこう。

"....."
"......"

なんか再帰版の方が書きやすいとか思ったのは気のせいか。。
でも再帰版の方は、マイナスきたら無限ループになっちゃいますね。

(b)1つのリストを引数とし、aというシンボルがいくつあるかを返す。
searchALoop::String -> Int
searchALoop str = length $ filter equalsA str

searchARecursive::String -> Int
searchARecursive "" = 0
searchARecursive (x:xs) = if equalsA x then 1 + searchARecursive xs else searchARecursive xs

equalsA::Char -> Bool
equalsA ch = ch == 'a'

main = do print $ searchALoop "abcad"
          print $ searchARecursive "abcad"

ループ版というか別にループしてないけど版は、lengthを使わざるを得ないような。
再帰版はlength使ってないので、無限リストに対応できるっていうことですかね。


結果はこう。

2
2