Haskellは数学【Haskell, 関数型プログラミング】

Haskellの真骨頂は関数型プログラミングにあります。

関数型プログラミングについては以下の記事で解説していると思いますので、 今回はHaskellの関数の文法についてお話します。

Haskellの関数の文法

Haskellの関数の構文は非常にシンプルです。

-- 関数の型宣言 -- 関数の本体

functionName :: ArgumentType1 -> ArgumentType2 -> ... -> ReturnType
functionName arg1 arg2 ... = 関数本体
  • 一行目の関数型の型宣言に関しては、引数を->でつなぎ、最後に関数から返却される型を表示します。
  • 二行目の関数本体は引数の列挙と関数が行う処理の記述を表します。

Haskellのサンプルコード

以下は文字列を結合する関数です。 String -> Stringconcatenateに渡される引数の型を表しており関数本体でのstr1str2に該当します。

-- 文字列を結合する関数
concatenate :: String -> String -> String
concatenate str1 str2 = str1 ++ str2

main = print (concatenate "Hello" "World") 

以下は引数が一つの関数で、doubleに数値を入れて呼び出すと2倍になって返却されます。 この場合、引数のIntが二倍になって返り値のIntとして返却されます。

-- 整数を2倍にする関数
double :: Int -> Int
double x = x * 2

main = print (double 10) 

Haskellは数学

Haskellの数学は関数の宣言に表れています。 Haskellの関数は写像ととらえることが出来るのです。

まずは数学における写像の定義は以下の通りです。

Xの要素(元)を変換機械fに通したら, 毎回同じYの要素(元)になるよ

これを数学的記号であらわすと以下のようになりますが、これはHaskellにおける関数の宣言と酷似していると思います

例えば、整数を二倍にする関数fの宣言は以下の通りでした。

f :: Int -> Int

これは「関数fIntで定義され,毎回同じIntの要素になるよ」 と読み取ることが出来ますが、これはもうほぼ写像です。

多少強引な意見に見えるかもしれませんが、Haskellは数学を元にした言語であると言えるのではないでしょうか?

Haskellの関数の合成

Haskellの関数を呼び出した結果をprintするとき、()でくくっていました。

main = print (double 10) 

これを別の言い方をするのであればHaskellでは意味のあるまとまりごとに()でくくると言い換えれます。 この()表現は、数学的な意味合いが強いです。

例えば、xに1を足す関数と、2倍する関数を合成するし数式であらわすとすると y = 2(x+1)となります。

これをHaskellの関数であらわしてみましょう。

-- 整数に1を足す関数
addOne :: Int -> Int
addOne x = x + 1

-- 整数を2倍にする関数
double :: Int -> Int
double x = x * 2

-- 数式 y=2(x+1) を表す
f :: Int -> Int
f x = (double (addOne x) )

main = print (f 10)

特に以下の行に注目してほしいのですが、数学の()と同様に優先順位が明確になっていると思います。

f x = (double (addOne x) )

関数を呼び出す際には()がトリガーとなるのですが、これが数学的な意味合いが強いことは覚えておいてください。

Haskellの関数合成記号

数学における関数の合成は、二通りの表現方法があります。

上記のように、でつなぐ方法がHaskellにはないのでしょうか?

結論から言えば、Haskellにも数学のような関数合成の記述方法があります。

-- g(x)
g :: Int->Int
g x = x + 1

-- f(x)
f :: Int->Int
f x = x*x

-- g(x)とf(x)の合成関数
fg :: Int->Int
fg x = (f . g) x

main = print (fg 10)

このように、Haskellにおける関数合成は.演算子を用いて行うことができます。

Haskellの再起関数

wikipediaによると、数学における階乗の定義は次のように表すことができます。

これは再帰的な階乗の定義ですが、これをHaskellであらわした場合、次のような表記になります。

f 0 = 1 
f n = n * fact (n - 1)

Haskellでのコードの記述とWikipediaの数式が似ていることに気づいたでしょうか? Haskellでは数式を書くように関数を記述できるのです

詳しい内容を一行ずつ説明します。

f 0 = 1

これは、nが0の場合、fの値は1であると定義しています。階乗の定義では、0の階乗は1となります。

f n = n * fact (n - 1)

ここでは、nが0でない場合のfの定義を行っています。この部分が再帰的な定義です。nが0でない場合、f nnf (n - 1) の積として計算されます。つまり、nの階乗は n(n-1) の階乗の積として定義されています。

子のコードは再帰的な表現をしているため、これを展開すると以下のようになります。

f 5 = 5 * f 4
    = 5 * (4 * f 3)
    = 5 * (4 * (3 * f 2))
    = 5 * (4 * (3 * (2 * f 1)))
    = 5 * (4 * (3 * (2 * (1 * f 0))))
    = 5 * (4 * (3 * (2 * (1 * 1))))
    = 120