第1回スタートHaskellの範囲を予習しました。
プログラミングHaskellの2章から4章まで予習しました。
つまづいた点や練習問題の解答を書いておきます。
間違っている箇所が多々あると思います。
- 第2章 はじめの一歩
関数適用の仕方とかコメントの書き方といった基本中の基本です。
この章でつまずいたのはレイアウト規則でした。
「行頭揃えで定義をグループ化できる。」って書いてありますが、
これだけだと改行をどこに入れてもよいのかわかりません。
プログラミングHaskellの例文を変形してghciでロードしてみたのですが、
どうもwhere句は行頭がバラバラでもいいようです。
行頭が揃っている例文はコンパイルできるのですが、
a = b + c where b = 1 c = 2 d = a * 2プログラミングHaskellの例文
これはghciで普通にロードできる。
こういう風にバラバラにしてもghciは通してくれます。
a = b + c where b = 1 c= 2 d = a * 2
a = b + c where b = 1 c= 2 d = a * 2
何度か試行錯誤を繰り返した結果こんな経験則が得られました。
- where は1カラム目に置いてはいけないが、1カラム目でなければどこから始めてもよい。
これは許されないが、
a = b + c where a = 1 b = 2=より前のカラムから始めてもghciは通してくれる。
a = b + c where a = 1 b = 2
- where に続く変数は where と同じカラムから始めてはいけないが、whereより前から始めてもよい。
これはghciが通してくれる。
where a = 1where a = 1
- where で定義した2つ目変数は一つ目の変数と同じカラムからだけでなく、1カラム目から書き始めてもよい。
これはghciが通すが、
where a = 1 b = 2これは許されない。
where a = 1 b = 2
- =の後に続く関数定義は空白文字の代わりに改行を使えるが、開業直後に1カラム目からは始められない。
これはghciが通すが、
a = b + c where b = 1 c = 2これは許されない。
a = b + c where b = 1 c = 2
初心者なのでわかりませんが、本当は内部で何か一貫したルールがあるのだと思います。
さて、このようにわかりづらくレイアウトをする事が許可されたとしてもわざわざ分かりづらく書く必要はありません。
Haskellでプログラムを書く時は本文の例に従って記述するべきだと思います。
2章の練習問題3.4.5もやってみました。
ex 3
- 変数は小文字にしなければならない
- 中置記法にする場合にはバッククオートを用いる
- 行頭を揃える
ex 4
lastはghciで既に使われていたのでmylastという名前にしました。mylast :: [a] -> a mylast xs = xs!!i where i = (length xs) - 1
ex 5
initはghciで既に使われていたのでmyinit0とmyinit1という名前で2種類定義しました。myinit0 :: [a] -> [a] myinit0 xs = take i xs where i = length xs - 1 myinit1 :: [a] -> [a] myinit1 xs = reverse (tail (reverse xs))
- 第3章 型とクラス
ここはHaskellの用語が理解できずに躓きました。
3章の題名のは「型とクラス」ですが型とクラスは別物です。
C/C++でプログラミングの勉強を始めたのでクラスと型は同じものだと思っていました。
Haskellではクラスは型の集合であって、型の集合から文脈的に選び出されるものが型です。
そしてその型の集合はどのようなメソッドを持っているかで決まります。
このHaskellのクラスという概念はJavaで言うインタフェースと似ていますが、ちょっと違うようです。
Javaではインタフェースを継承したクラスのインスタンスはインタフェース型のインスタンスとして扱う事が出来ますが、
Haskellのクラスは型の集合であって型ではないためHaskellのクラスのインスタンスは型になります。
そしてクラスのインスタンスを束縛する変数が多相型や多重定義型の説明で出てくる型変数だと思います。
練習問題1,2,4の解答を書いておきます。
ex 1
['a','b','c'] の型は [Char] ('a','b','c') の型は (Char, Char, Char) [(False, 'o'), (True, '1')] の型は[(Bool,Char)] [tail, init, reverse] の型は [[a]->[a]]
ex 2
second :: [a] -> a second xs = head (tail xs) swap :: (a, b) -> (b,a) swap (x,y) = (y, x) pair :: a -> b -> (a, b) pair x y = (x, y) double :: Num a => a -> a double x = x * 2 palindrome :: Eq a => [a] -> Bool palindrome xs = reverse xs == xs twice :: (a -> a) -> a -> a twice f x = f (f x)
ex 4
任意の数の関数が同等である場合、
それらの関数はあらゆる引数に対して同じ値を返す。取りうるすべての引数で戻り値を比較する事ができないため、
関数の同等性を確認する事は出来無い。
ただし、引数を取らずに定数を返す関数の同等性は確認できる。testfunc :: Int testfunc = 10 constx :: Num a => a constx = 20
- 第4章
ここで躓いたのは演算子の定義の仕方です。
演算子の定義には記号(|とか&)しか使えないようです。
本文中では他に気になったところはなかったと思います。
練習問題の解答は以下のとおりです。
ex 1
ここでわからなかったのはwhere句の置く場所です。
whereは最後に一箇所だけでしか使えないようです。halve :: [a] -> ([a],[a]) halve xs | ((length xs) `rem` 2) == 0 = (x,y) | otherwise = ([],[]) where x = take ((length xs) `div` 2) xs y = drop ((length xs) `div` 2) xs
ex 2
safetail0 :: [a] -> [a] safetail0 xs = if (null xs) then [] else tail xs safetail1 :: [a] -> [a] safetail1 xs | null xs = [] | otherwise = tail xs safetail2 :: [a] -> [a] safetail2 [] = [] safetail2 xs = tail xsex 3
を複数定義するために を追加した演算子を定義しました。 (|||) :: Bool -> Bool -> Bool (|||) True True = True (|||) True False = True (|||) False True = True (|||) False False = False (||||) :: Bool -> Bool -> Bool (||||) False False = False (||||) _ _ = True (|||||) :: Bool -> Bool -> Bool (|||||) x y | x == True = True | y == True = True | otherwise = False> (|||.) :: Bool -> Bool -> Bool (|||.) True _ = True (|||.) _ True = True (|||.) _ _ = Falseex 4
(&&&) :: Bool -> Bool -> Bool (&&&) x y = if x == True then if y == True then True else False else Falseex 5
(&&&&) :: Bool -> Bool -> Bool (&&&&) x y = if x == True then y else Falseex 6
mult x y z = (\a b c -> a * b * c) x y z