Haskellの特徴
-
純粋関数型言語
全ての関数が副作用を持たず、同じ入力には常に同じ出力となるため、コードの予測が容易 -
静的型付け
型安全性が高く、型エラーがコンパイル時に検出できるため、信頼性と可読性に優れている -
遅延評価
式が必要になるまで評価されないため、無限リストの利用など高度な抽象化が可能 -
関数型言語ならではの強力な言語機能が豊富
パターンマッチングや高階関数、カリー化、リスト内包表記、型クラス等 -
モナド
副作用(入出力や状態の変化など)を安全に扱える設計(IOモナドなど) -
主な処理系はGHC(Glasgow Haskell Compiler)
Cabalなどのパッケージ管理システムを持つ
基礎プラクティス
今回はとにかく触ってみるのが目的なので細かいことは置いておいてとりあえずコードを書いてみましょう!
1. シンプルな関数定義
まずは関数の定義から。引数で受け取った数字を2倍する関数を作成します。
-- プログラム実行時に呼び出されるmain関数
main = do
print (double 2) -- 関数の呼び出し方
-- 引数で受け取った数字を2倍
double x = x * 2
2. 再帰関数
Haskellにおいては関数で副作用を持つことができないため、変数を使って値を保持しておくということができません。再起的に呼び出す関数を使うことが多いようです。
引数で受け取ったリストの長さを求める関数を作ってみます。
main = do
print (getLength [1, 2, 3])
-- リストの長さを求める再帰関数
getLength :: [a] -> Int
getLength [] = 0
getLength (_:xs) = 1 + getLength xs
1行目: 型 a の任意のリストを受け取り、整数を返すことを定義
2行目: リストが空なら0を返す(ここが再帰の終了条件)
3行目; それ以外なら「1」+残りの長さ(再起的に関数を呼び出す)
ちなみに、_:xsは_がリストの最初の値(処理に必要ないので破棄している)で、xsが残りのリストの値を指しています。
実際の動きは以下の流れで実行されます。
- 最初に _:[2][3] にマッチ → 1 + getLength [2][3]
- _:[3] にマッチ → 1 + getLength [3]
- _:[] にマッチ → 1 + getLength []
- [] にマッチ → 0(ここが終了条件)
計算式はこんな感じ
1 + (1 + (1 + 0)) = 3
3. リスト内包表記
Haskellでは既存のリストから新しいリストを簡潔に作成するための構文であるリスト内包表記を使うことができます。数学の集合の内包表記に由来しているそう。
1~10までのリストから偶数のみを抽出する関数を作成します。
main = do
print (evens)
-- 1~10までの偶数を抽出
evens = [x | x <- [1..10], x `mod` 2 == 0]
x <- [1..10] : 1から10まで順番に x に入れる
x mod 2 == 0 : その x が2で割り切れる(偶数)場合だけ使う、という条件
[x | ... ] : 条件を満たした x だけを集めて新しくリストにする
4. パターンマッチングで条件分岐
基礎的な条件分岐を実現する関数も作ってみます。
引数で受け取った文字列が"Yes"かどうかを判定する関数です。
main = do
print(isYes "Yes") -- True
print(isYes "No") -- False
-- パターンマッチでYes/Noを判定
isYes "Yes" = True
isYes _ = False
参考