プログラミングHaskell第3章(型とクラス)まとめ
基本型
Bool (真理値)
False, True.
Char (文字)
シングルクォートで囲む。他の言語同様。
String (文字列)
ダブルクォートで囲む。他の言語同様。
Float (単精度浮動小数点)
Double (倍精度浮動小数点)
他の言語同様.
リスト型
T型の要素を持つリストの型を[T]と書く。各要素は共通の型でなければならない。
Hugsやghciで確かめるには:type の後に、式を書く。省略して:t だけでもおk。
Prelude> :t [1, 2, 3] [1, 2, 3] :: Num t => [t] Prelude> :t ['1', 2, 3] <interactive>:1:7: No instance for (Num Char) arising from the literal `2' Possible fix: add an instance declaration for (Num Char) In the expression: 2 In the expression: ['1', 2, 3]
型Tに制限は無いので、リストのリストとかもあり。リストの長さは、length関数で取得。
Prelude> :t length length :: [a] -> Int Prelude> length [] 0 Prelude> length [[]] 1 Prelude> length [[0],[1,3]] 2
タプル型
有限この要素の組。各要素の型は異なっても良い。
番目の要素が型を持つとき、そのタプルの型は、となる。
型に要素数の情報を含んでいる。要素数0のタプルは`ユニット`と呼ばれる(意味の無い値を示す)。
また、式の評価順序を明示的に指定する括弧と区別がつかないため、`要素数1のタプル型はない`。
リスト同様、タプルのタプルや、その他の型との組み合わせが可能。
関数型
Haskellでは、全ての式が型を持ち、型推論によって、式を評価する前に決定される。
fが型Aを型Bへ変換する関数であり、eが型Aの式であれば、関数適用f eは、型Bをもつ
型の引数をとり、型に変換(写像)する関数の型をと書く。
f :: A -> B e :: A f e :: B
Hugsやghciで確かめるには:type の後に、式を書く。省略して:t だけでもおk。
Prelude> :t not not :: Bool -> Bool Prelude> :t False False :: Bool Prelude> :type not False not False :: Bool Prelude> import Data.Char Prelude Data.Char> :t isDigit -- Data.Charをインポートする必要あり isDigit :: Char -> Bool
ちにみに、ghciで関数定義したい場合は、letを使って書かなければならない。(I/Oモナドの中だから?)
また、複数行にまたがって、書きたい場合は、:{ :}を使う。
以下は、二つのInt引数を取って、その和を返す関数の定義例
(厳密には、Haskellでは、引数は一つしかとらない。 二つの引数を取ると言っているが、この場合、タプル型(bigram)の引数を一つ取っているということ。 だと思う)
Prelude> :{ Prelude| let { Prelude| add :: (Int, Int) -> Int; Prelude| add (x,y) = x + y Prelude| } Prelude| :} Prelude> add (5, 6) -- add 5 6では駄目。後述する「カリー化」されていないから 11
関数には、引数の型と結果の型に制限が無い。
全ての入力に対して、出力の値が定義されている関数を全域関数というが、
Haskellでは、関数は全域関数である必要は無い。つまり、入力によっては出力の値が未定義であっても良い。
(例えば、headは空リストに対して定義はされていない)
カリー化と部分適用
※何となく分かったことを書いてるので、誤ったことを書いている可能性があります。
ググってみると、いくつか定義があるみたい。 参考:http://d.hatena.ne.jp/kazu-yamamoto/20110906/1315279311
Groovyの標準ライブラリ自体がカリー化を行うための関数ではないものにcurryとか付けてるので、
部分適用だと誤用して書かれてることが多いそうな。
↓部分適用とカリー化の違いについては、ページが分かりやすかったです。
参考:http://kmizu.hatenablog.com/entry/20091216/1260969166
プログラミングHaskellでは複数の引数という表現を使っているので、
それに習って、私も複数の引数とという表現を使うことにしますが、語弊があるかも知れません。
Haskellでは厳密には引数は一つ.
(タプルやリストで指定する複数の要素のことや、カリー化されている関数fに対して、f x y zのx,y,x etc. を複数の引数といっている場合があります。多分...)
参考 http://d.hatena.ne.jp/kazu-yamamoto/20110906/1315279311
カリー化(currying)
複数の引数をとって値を返す関数を、一引数を取る関数のチェインに直すことをカリー化するという。
参考http://qiita.com/f81@github/items/e8bfab96b4be9e404840
例:
Prelude> let { add' :: Int -> (Int -> Int); add' x y = x + y } Prelude> (add' 5) 6 11 Prelude> :t add' 1 add' 1 :: Int -> Int -- (add' 1)は(Int -> Int)型の関数を返すことが分かる Prelude> let { trimul :: Int -> (Int -> (Int -> Int)); trimul x y z = x * y * z } Prelude> ((trimul 2) 3) 4 24
上は()をつけて書いてあるが、->は右結合で、関数適用は左結合なので()は不要。
Prelude> :t add' add' :: Int -> Int -> Int Prelude> trimul 2 3 4 24
また、二つ組を取る関数をカリー化する関数が標準で用意されているので、そちらを使うともっと簡単になる。
Prelude> let { sub :: (Int, Int) -> Int; sub (x,y) = x - y } Prelude> let sub' = curry sub Prelude> :t sub' sub' :: Int -> Int -> Int Prelude> sub' 3 10 -7 Prelude> :t curry curry :: ((a, b) -> c) -> a -> b -> c
ちなみにカリー化の語源はHaskell Brooks Curry(カリー化の考案は別の人だった気がしますが)
多相型
任意の型を含む型。任意の型を含む型や式は多相型と呼ばれる。
任意の型は、小文字で始まらなければならない。通常、a,b,cを使う。
多相型の例
fst :: (a, b) -> a -- 組の1番目の要素を取り出す snd :: (a, b) -> b -- 組の2番目の要素を取り出す head :: [a] -> a -- 空でないリストの先頭要素を取り出す last :: [a] -> a -- 空でないリストの末尾要素を取り出す init :: [a] -> [a] -- 空でないリストから先頭要素を取り除く tail :: [a] -> [a] -- 空でないリストから末尾要素を取り除く take :: Int -> [a] -> [a] -- リストの先頭からn個の要素を取り出す drop :: Int -> [a] -> [a] -- リストの先頭からn個の要素を取り除く zip :: [a] -> [b] -> [(a, b)] -- 二つのリストから組のリストを作り出す id :: a -> a -- 恒等関数.引数をそのまま返す
zipの使用例
Prelude> let l1 = [1,2,3] Prelude> let l2 = [4,5] Prelude> zip l1 l2 [(1,4),(2,5)]
多重定義型
1つ以上の暮らす制約を持つ型。
(-) :: Num a => a -> a -> a -- Numクラスのインスタンスである任意の型aに対し、型a -> a -> aを持つ signum :: Num a => a -> a -- 符号反転 negate :: Num a => a -> a -- 正負の符号(正->1, 0->0, 負->-1) 3 :: Num a => a -- 数値自体も多重定義されている
基本クラス
Eq-同等クラス
==, /=で比較できる値を持つ型の集合。基本型や、要素がEqのインスタンスであるリスト、タプルは、全てEqに属す。
Prelude> 1 == 1 True Prelude> [1,2] == [1,2,3] False Prelude> ('a', False) /= ('a', True) True Prelude> "abc" == "abc" True
Ord-順序クラス
Eqのインスタンスであることに加え、値が線形的に順序つけられる型の集合。<, <=, >, >=, min, maxで比較できる。
基本型や、Ordのインスタンスを要素にもつリスト、タプルは、全てOrdに属す。文字列、リスト、タプルは辞書順で比較される。
Prelude> min "abd" "abc" "abc" Prelude> max [1,2,3] [1,3] [1,3] Prelude> 'b' > 'a' True Prelude> ('a', 2) < ('a', 3) True
Show-表示可能クラス
showメソッドを用いて値を文字列へ変換可能な型の集合。基本型や、Showのインスタンスを要素にもつリスト、タプルは、全てShowに属す。
Prelude> :t show show :: Show a => a -> String Prelude> show ('a', False) "('a',False)"
Read-読込可能クラス
readメソッドを用いて文字列から値へ変換可能な型の集合。基本型や、Readのインスタンスを要素にもつリスト、タプルは、全てReadに属す。
Prelude> :t read read :: Read a => String -> a Prelude> read "'a'"::Char 'a' Prelude> read "-1"::Int -1 Prelude> read "[1,2,3]"::[Int] [1,2,3] Prelude> read "('a',False)"::(Char,Bool) ('a',False) Prelude> read "False"::Bool False Prelude> read "False" -- 型指定がないのでエラー エラー 略 Prelude> not (read "False") -- 文脈からBoolに型推論されるのでエラーにはならない True Prelude> not (read "Hello") -- 文脈からBoolとして読み込もうとするが、Boolに変換できないためエラー *** Exception: Prelude.read: no parse
Num-数値クラス、
EqとShowのインスタンス且つ+, -, *, negate, abs, signumで処理可能な数値を値として持つ型の集合。
Numは除算のメソッドを提供していない。
Prelude> -3 + 4 1 Prelude> 6.4 *6.7 42.88 Prelude> "fasdlh" + "fds" エラー 略 Prelude> [1,2] + [3] エラー 略 Prelude> 'a' + 'b' エラー 略