理想未来ってなんやねん

娘可愛い。お父さん頑張る。

Haskell 10日目

Haskell 10日目。

ちょっと間が空きましたが、7章の続きから勉強していきます。

if式

if式の文法

条件判断を行う構文です。if式は次のように書きます。

if 条件式 then1 else2

条件式はBool型でなければいけません。
式1と式2あいずれも式であって、コードブロックではないことに注意。

if式のコーディングスタイル

if式は次のようなスタイルで書くこともできます。

if c == '\t' then '@' 
             else c
if c == '\t' 
  then '@' 
  else c

パターンマッチ

パターンマッチ(pattern match)とは、値のパターンによる場合分けのことです。
値を調べると同時に、値の一部に変数を束縛することもできます。

パターンマッチを使った定義の例

map :: (a -> b) -> [a] -> [b]
map f []     = []
map f (x:xs) = f x : map f xs

『map f []』と『map f (x:xs)』の部分にパターンマッチが使われています。
『f』はどのような値にでもマッチし、その値に変数fを束縛するパターン
[]』は空リストにマッチするパターン
『(x:xs)』は空でないリストにマッチして、第1要素を変数xに、第2要素以降のリストを変数xsに束縛するパターンです。

様々なパターン

パターンマッチには以下のパターンが存在します。

  • 変数パターン
  • 『_』パターン(ワイルドカード)
  • リテラルパターン
  • タプルパターン
  • リストパターン
  • データコンストラクタパターン
  • 『@』パターン

また、以下のパターンも存在しますが、Haskellの仕様書では非推奨とされています。

  • n+kパターン
  • 遅延パターン
変数パターン(variable pattern)

変数パターンを使った関数定義の例

id :: a -> a
id x = x
『_』パターン

『_』パターンを使った関数定義の例

const :: a -> b -> a
const x _ = x

『_』パターンは別名ワイルドカード(wildcard)とも言います。
引数『_』が『_』パターンです。
『_』パターンは任意の値にマッチしますが、変数を束縛しません。
関数の引数は必要だが、具体的な値は必要ないという場合に利用します。

リテラルパターン(literal pattern)

リテラルパターンを使った関数定義の例

expandTab :: Char -> Char
expandTab '\t' = '@'
expandTab c    = c

引数の『'\t'』がリテラルパターンです。
リテラルパターンは、値と、指定したリテラルが等しいかどうかを『==』演算子で検査し、等しいときにマッチします。
リテラルパターンに使えるリテラルは、数値リテラル、文字リテラル、文字列リテラルです。

タプルパターン

タプルパターンを使った関数定義の例

format :: (Int, String) -> String
format (n, line) = rjust 6 (show n) ++ " " ++ line

『(n, line)』がタプルパターンです。
タプルパターンはタプルにマッチするパターンで、タプルの各要素に対して任意のパターンを記述できます。

リストパターン

リストパターンを使った関数定義の例

last []     = error "last []"
last [x]    = x
last [_:xs] = last xs

[x]』がリストパターンです。
リストパターンはリストにマッチするパターンで、リストの各要素に対して任意のパターンを記述できます。

データコンストラクタパターン

データコンストラクタの使用例

map :: (a -> b) -> [a] -> [b]
map f []       = []
map f (x:xs) = f x : map f xs

[]』と『(x:xs)』がデータコンストラクタを使ったパターンです。
[]』は空リストを作成するデータコンストラクタです。
『:』は新しいリストを作成するデータコンストラクタパターンです。

『@』パターン(as-pattern)

『@』パターンを使った関数定義の例

lstrip str@(c:cs) = if isSpace c then lstrip cs else str

『str@(c:cs) 』で1つの値にマッチするパターンです。
こう書くと、(c:cs)というパターンにマッチさせつつ、その値全体に変数strを束縛させることができます。

ガード

ガードではBool型の任意の式を使って値を検査できます。

ガードを使った関数定義の例

joinPath :: String -> String -> String
joinPath a b
    | null a            = pathSep : b
    | last a == pathSep = a ++ b
    | otherwise         = a ++ pathSepStr ++ b

引数のパターンの後の、『|』(バー)と『=』の間がガードです。
ガードは上から順番に評価され、その値がTrueだったらガードに対応する定義が使われます。

パターンマッチとガード

パターンマッチとガードは、両方あわせて1つの条件です。
つまりガートがすべて失敗した場合は、それに対応するパターンも失敗します。

具体例

f []             = 0
f [x]    | x > 0 = 1
f (x:xs)         = 2

(f [1])の値は1
なぜなら2番目のパターンにマッチして、そのガードの値もTrueであるため。

(f [-1])の値は2
なぜなら2番目のパターンにマッチしますが、そのガードの値がFalseになるため3番目のパターンにマッチして値が2となります。



今日のところはここまで