理想未来ってなんやねん

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

Haskell 14日目 〜 定義と束縛 〜

Haskell 14日目。

本日は定義と束縛に進みます。

定義と束縛

定義と束縛に関わる構文を学びます。

let式

let式を使うと、その式の中でだけ有効な束縛を導入できます。

f n = let x = n + 1
          y = n + 2
          z = n + 3
       in x * y * z

この式の例では変数x,y,zを束縛しています。xの値は「n + 1」で、yの値は「n + 2」、zの値は「n + 3」で、let式を使って束縛した変数はそのlet式内の全体で有効となります。

また、let式の値はinの後に書いた式の値となり、上記の例ならばx * y * zとなります。

let式の構文は以下の通りです。

let 定義1
    定義2
    定義3
      :
in0

「定義n」は関数束縛かパターン束縛となります。

where節

where節を使うと、特定の定義内でのみ有効な束縛を導入できます。

下記の例では、where節を使って変数baseを1900に束縛しています。

resolveY2K y = base + y where base = 1900

一般にwhere節は次のように書きます。

定義0 where 定義1 定義2 定義3

定義0では、定義1、2、3……を使う事ができます。
「定義n」は関数束縛かパターン束縛です。

定義0が関数の定義であるときは、定義1、2、3……からその関数の引数を参照できます。
下記の例では、where節で導入した関数translateの定義の中から、expandTab関数の引数widthを参照しています。

expandTab :: Int -> String -> String
expandTab width cs = concatMap translate cs
  where
    translate '\t' = replicate width ' '
    tanslate c     = [c]
let式とwhere節の違い

let式とwhere節は機能としてはほとんど違わないようにも見えますが、以下の2点で異なります。

  1. let式は式なのでlet式自体も値を持ちます。しかしwhere節は節なので、値を持ちません。
  2. where節は複数のガードにまたがって有効です。

where節では以下のような場合でも、(1)、(2)、(3)すべてから関数squareが問題なく参照できます。let式ではこのような事はできません。

foo i | i > 0  = square (i + 1) -- (1)
      | i == 0 = square 0       -- (2)
      | i < 0  = square (i - 1) -- (3)
  where
    square n = n * n

尚、where節が有効なのは複数のガードであって、複数のパターンではない事に注意が必要です。
以下の例はwhere節ではうまくいかない例です。

bar []     = square 15 -- ここからはsquareが参照できない。
bar (x:xs) = square x
  where
    square n = n * n

以上の2点の違いから、それぞれの使われ方に次の傾向があります。

  • let式は単純に新しい変数を導入するために使われる。
  • where節は関数内で使うための関数を定義するのに使われる。

変数のシャドウイング

関数の引数と同じ名前の変数をlet式で導入すると、そのlet式内ではlet式で導入したyを参照するようになります。
これをシャドウイング(shadowing)と言います。

以下の例ではlet式内でシャドウイングが行われています。

f x y = let y = x * x
        in y - 1

シャドウイングが起こるのは関数の引数だけでなく、let式がネストした場合、より内側のlet式で導入した変数が外側のlet式の変数を覆い隠します。

定義内での相互参照

let式やwhere節では、同じ構文で導入した束縛を定義の中から参照できます。
次のコードでは変数yの定義のために、同じlet式で束縛した変数xを使っています。

let x = 5
    y = x + 2
in x * y

この例では下に書いた定義から上の定義を参照しましたが、逆に上に書いた定義から下の束縛を参照する事もできます。

パターン束縛

Haskellでは関数の仮引数は単なる変数ではなく、値のパターンを記述できました。
それと同じように、let式やwhere節でもパターンを使って変数を束縛できます。

let (x:xs) = [1, 2, 3]
in xs

「let (x:xs) = …」という部分がパターンを使った束縛です。これをパターン束縛(pattern bindings)と言います。文字列をパターン(x:xs)で受けて、変数xとxsを束縛しています。結果として1に変数xを束縛し、[2, 3]に変数xsを束縛します。


長かった7章もやっと終わりです。
次から8章 関数に入ります。


今日のところはここまで。