blog1.mammb.com
の続きです。
関数の定義
2 つの数字を加算する add 関数を定義してみます。
add x y = x + y
実行すると、以下のように 2 つの数字を加算した結果が得られていることが分かります。
Main> add 2 3 5
この関数の型について調べてみましょう。:type コマンドを使います。または :t としても同様です。
Main> :type add add :: Num a => a -> a -> a
この型の定義は、-> が右結合であるので、以下と同じ意味となります。
add :: Num a => a -> ( a -> a )
この関数の型定義の意味は、
- 「add ::」 add 関数の定義は、
- 「Num a =>」 型クラス Num という型制約を持つ型変数 a に対して、
- 「a -> 」 a の型を入力として、
- 「( a -> a )」 a の型を a の型に変換する関数 を返す
となります。この a は Java で言う総称型のような感じで、以下の Java コードの T のようなものと考えると良いでしょう。
public <T extends Number> T add(T x, T y) { return x + y; }
関数定義を追うと、
実際の関数定義「add x y = x + y」に対して「add 2 3」のように引数を与えた場合で考えると、
- Num クラスである 2 を引数 x として与えると、
- y を引数に取る 2 + y という関数が返却されて、
- この関数 2 + y という関数に、3 を引数 y として与えると、
- 2 + 3 となり、結果 5 となる
となります。
もう一度、型定義について見てみると、
Main> :type add add :: Num a => a -> a -> a
a を ( a -> a ) という関数に変換する、つまり 引数を1つ与えると関数を返す こととなり、これをカリー化されていると呼びます。関数型言語では、入力として関数を取る関数 も定義でき、これらを含めて高階関数と言います。
普段は、a 型と a 型の引数を取って、結果として a 型を返すんだな、、のように考えます。
さてここで、 Num クラスとはなんでしょうか。まずは型について見ていき、その後でクラスについて見ていきます。
Haskellの型
基本型
Haskell が提供する基本的な型には以下があります。
- Bool 真理値
- 2つの真理値、False と True を持つ
- Int 整数
- 普通の整数型
- Integer 多倍長正数
- 上限と下限の制限がない整数型で Java で言う BigInteger
- Float 単精度浮動小数点数
- 単精度の浮動小数
- Double 単精度浮動小数点数
- 倍精度の浮動小数
- Char 文字型
- 文字を表し、'A' のようにシングルクオートで囲む
- String 文字列型
- 文字列を表し、"foo"のようにダブルクオートで囲む
Haskell の型名は先頭が大文字となります。
例えば、引数として Int を取り Int を返す関数 plusOne の型定義は以下のようになります。
plusOne :: Int -> Int plusOne x = x + 1
型定義を省略した場合には、Haskell の型推論により最も広い型が型推論されます。関数の型定義を記載することで、意図が明確になりエラーの早期発見につながるため、上の例のように極力型定義を記述するのが望ましいと思います。
定数を定義し、その定数の型を指定するには、以下の2通りの指定ができます。
pi :: Float pi = 3.14 pi = 3.14 :: Float
リスト型
リストは[]で定義し、要素はカンマで区切ります。
['a', 'b', 'c'] ::[Char] -- Char のリスト ["ab", "cd"] ::[String] -- String のリスト
リストのリストも扱えます。また[]は要素がゼロの空リストとなります。
[["ab"], ["ab", "cd"]] -- リストのリスト [] -- 空リスト
タプル型
タプルは()で定義し、要素はカンマで区切ります。複数の型を束ねて扱えます。
(False, True) :: (Bool, Bool) ("ab", True, 'z') :: (String, Bool, Char)
リストを要素に持つこともできます。
(['a', 'b'], [True, False]) :: ([Char], [Bool])
関数型
関数の型は、ある型の入力を ある型の出力にする、変換を定義することになります。変換は -> にて表されます。
ゼロから n までの整数のリストを返す zeroto 関数は以下のように定義できます。
zeroto :: Int -> [Int] zeroto n = [0..n]
入力として Int 型を与えると、出力として Intのリスト型が返されます。
関数の型は、型変数にて多相的に定義できます。リストから先頭要素を取り出す関数 head の型は以下のように定義できます。
head :: [a] -> a
型変数は、a, b, c という名前が使われるのが一般的です。Java での List
型クラス
型クラスとは、共通のメソッドを持つ型をまとめて名前を付けたものです。
例えば あるデータ要素を並び替える場合に、並べ替えに必要な関数が定義されている型に Ord という名前をつけて扱います。Ord という型クラスは並び替えに必要な関数を持つ共通のインターフェースとして抽象的に扱うことができます。
例えば、Bool, Char, String, Int のような基本型はすべて Ord クラスのインスタンスとなります。
Eqクラスを例に
Eq クラスは同値比較可能な型の集合です。定義は以下のようになっています。
class Eq a where (==), (/=) :: a -> a -> Bool x == y = not (x/=y) x /= y = not (x==y)
class とは == と /= 関数が定義された型を表し、Java でいうとインターフェースに近いです。
このEqクラスのインスタンスとして、例えば Bool 型があり、以下のような定義で表されます。
data Bool = False | True deriving (Eq, Ord, Ix, Enum, Read, Show, Bounded)
Bool 型は、Eq, Ord, Ix, Enum, Read, Show, Bounded 型クラスのインスタンスとして定義されています。
代表的な型クラス
- Eq クラス
- 同値比較可能なクラスで、(==)、(/=) の定義を持つ
- Ord クラス
- 順序付け可能なクラスで、(<)、(>) など、比較関数の定義を持つ
- Num クラス
- 数値クラスで、(+)、(-) など、数値演算の定義を持つ
- Integral クラス
- 整数クラスで、div、mod の商と余り演算の定義を持つ
- Show クラス
- 表示可能クラスで、show にて文字列への変換関数の定義を持つ
- Read クラス
- 読込可能クラスで、read にて文字列から型への変換関数の定義を持つ
blog1.mammb.com
につづく