kanetaiの二次記憶装置

プログラミングに関するやってみた、調べた系のものをQitaに移して、それ以外をはてブでやる運用にしようと思います。http://qiita.com/kanetai

プログラミングHaskell第3章(型とクラス)まとめ

基本型

Bool (真理値)

False, True.

Char (文字)

シングルクォートで囲む。他の言語同様。

String (文字列)

ダブルクォートで囲む。他の言語同様。

Int (固定制度整数)

固定長整数。プログラミングHaskellによると、Hugsでは [-2^{31}, 2^{31})らしいが、

Integer (多倍長整数)

java.math.BigIntegerみたいなもん。
必要なだけメモリーに格納され、上限、下限を気にする必要は無い。おお、便利便利。

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

タプル型

有限この要素の組。各要素の型は異なっても良い。
 i番目の要素が型 T_iを持つとき、そのタプルの型は、 (T_1, T_2, \cdots ,T_n)となる。
型に要素数の情報を含んでいる。要素数0のタプルは`ユニット`と呼ばれる(意味の無い値を示す)。
また、式の評価順序を明示的に指定する括弧と区別がつかないため、`要素数1のタプル型はない`。

リスト同様、タプルのタプルや、その他の型との組み合わせが可能。

関数型

Haskellでは、全ての式が型を持ち、型推論によって、式を評価する前に決定される。
fが型Aを型Bへ変換する関数であり、eが型Aの式であれば、関数適用f eは、型Bをもつ
 T_1の引数をとり、型 T_2に変換(写像)する関数の型を T_1 \to T_2と書く。

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(カリー化の考案は別の人だった気がしますが)

部分適用(partial (function) application)

複数の引数を取る関数に対して、一部の引数に値を束縛した関数を返すこと
複数の引数というのはry
カリー化された関数を全個数よりも少ない引数に部分適用する例:

Prelude> let increment = add' 1 -- 部分適用
Prelude> :t increment           -- Int値xを一つとって++xした値をを返す関数の出来上がり
increment :: Int -> Int
Prelude> increment 120
121

多相型

任意の型を含む型。任意の型を含む型や式は多相型と呼ばれる。
任意の型は、小文字で始まらなければならない。通常、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'
エラー 略
Integral-整数クラス

Numのインスタンスかつ、値が整数かつ、div, modメソッドを提供する型の集合。
Int, IntegerはIntegralのインスタンス

Prelude> 7 `div` 2
3
Prelude> 7 `mod` 2
1
Prel
Fractional-分数クラス

Numのインスタンスかつ、値が整数でなく、/, recip(逆数)メソッドを的供する型の集合。
Float, DoubleはFractionalのインスタンス。※FloatはFloatingクラスのインスタンスでもある。

Prelude> 7 / 2
3.5
Prelude> recip 2
0.5