第3回 もう少し関数の話をしよう

山下 伸夫
株式会社タイムインターメディア

2009/1/14

関数束縛

 bmicの定義本体をもう一度見てみよう。型シグネチャとの対応も併せて見てほしい。

bmic :: Height -> (Weight -> BMI      )
bmic = \ h     -> (\ w    -> bmi (h,w))

 等式の左辺の変数bmicは関数の名前として機能する。右辺はHeight型の値から(Weight -> BMI)型の値への対応を付ける関数の値(を表す式)である。上の定義方法は値に名前を付けるという直截(ちょくせつ)な方法である。この等式は、左辺の変数を右辺の値に束縛しているので、変数束縛ということがある。

 変数の値が関数である場合、関数適用を結果の値に束縛するという形式の等式で関数名を定義することができる。

bmic :: Height -> (Weight -> BMI     )
bmic    h      = \ w      -> bmi (h,w)

 2つの関数適用を結果の値に束縛するという形式の定義も可能である。

bmic :: Height -> (Weight -> BMI)
(bmic   h     )    w      =  bmi (h,w)

 ->の右結合性と関数適用の左結合性があるので、Haskellの慣例では括弧を省略する。

bmic :: Height -> Weight -> BMI
bmic    h         w      =  bmi (h,w)

 このような関数適用を結果の値に束縛するという形式の等式のことを関数束縛という。関数束縛の等式の左辺で導入された名前のうち、引数の部分で導入された名前はその関数束縛の等式の右辺でのみ有効であることに注意すること。

 また、繰り返すが上のbmicの関数束縛の等式の左辺における関数適用の形式では、関数適用が2つ、それぞれに引数が1つずつである。

 上のbmicの関数束縛による定義をしている等式の右辺をじっと眺めてみよう。

bmi (h,w)

 この式には3つの名前(変数)がある。bmi、h、wである。このうち、hとwは関数束縛の等式の左辺、引数の位置に現れている。この位置に現れる変数は、λ変数といい、関数が具体的な値(を表す式)に適用されたときに初めて束縛される。実際ghciで字面どおり評価しようとするとエラーが起こる。

*BMI> bmi (h,w)

<interactive>:1:5: Not in scope: `h'

<interactive>:1:7: Not in scope: `w'

 これは、h、wという変数の束縛(定義)が有効範囲で見当たらないという趣旨のエラーメッセージである。bmiはBMIモジュールで定義されているので、このモジュールがロードされている状態なら、エラーにはならない。

 逆にbmiを定義しているモジュールがロードされていない状態でなら、当然、bmiという変数の束縛(定義)が有効範囲にないという旨のエラーとなる。ghciでは、:moduleコマンドを使ってモジュールをアンロードすることもできる。

*BMI> :module - BMI
>

 プロンプトが*BMI> から、> に変化した。これは、BMIモジュール(とPreludeモジュール)が見えている環境から、何のモジュールも見えていない環境に変わったことを表している。

> bmi (h,w)

<interactive>:1:0: Not in scope: `bmi'

<interactive>:1:5: Not in scope: `h'

<interactive>:1:7: Not in scope: `w'

 ここで、bmiもλ変数にした関数抽象を考えよう。

\ bmi -> (\ h -> (\ w -> bmi (h,w) ))

 λ変数の名前を変えても意味は変わらないので、bmiの代わりにfを使うことにする。

\ f -> (\ h -> (\ w -> f (h,w) ))

 この関数の意味を考えてみよう。fが示す値から、\ h -> (\ w -> f (h,w))という値への変換である。f (h,w)という関数適用式があるので、fは関数であることが分かる。

 だから、上の式は関数から関数への対応を示す関数である。この式が表す値にfun2Funという名前を付けよう。ghciではlet文を使って、以降有効な束縛を作ることができる(このlet文の機能は、Haskellの機能ではなく、ghciという対話型インタプリタ特有の機能である。Haskellのlet式やlet構文とは違うことに注意)。

> let fun2Fun = \ f -> (\ h -> (\ w -> f (h,w) ))

 これで上の関数がfun2Funという名前で参照できるようになった。ではfun2Funの型を調べてみよう。

> :type fun2Fun
fun2Fun :: ((t, t1) -> t2) -> t -> t1 -> t2

 先に説明したように型シグネチャの->の結合性に従って、括弧を補うと

fun2Fun :: ((t, t1) -> t2) -> (t -> (t1 -> t2))

ということになる。

 つまり、この関数fun2Funは(t, t1) -> t2という型の関数から、t -> (t1 -> t2)という関数への対応を示す。型シグネチャに現れている型名はt、t1、t2の3つで、それぞれ小文字で始まっている。

 前回までに出てきた型名といえば、Double、BMI、Height、Weightのように大文字で始まっていた。小文字で始まる型名は型変数といい、どのような具体的な型(大文字で始まる名前を持つ型構成子)でも置き換え可能であることを示している。ただし、1つの型シグネチャの中で出現する同じ名前の型変数は、同じ型構成子で置き換える。

 fun2Funは、1つの値を導くのに2つのパラメータを含むペア型の引数を取る関数を、同じ1つの値を導くのに2回の関数適用を行う関数に変換する関数である。これはPreludeですでに定義済みのcurryという関数と同じ関数である。

2/3

Index
もう少し関数の話をしよう
  Page1
関数適用
関数の型
Page2
関数束縛
  Page3
関数を作るという感覚

のんびりHaskell

 Coding Edgeお勧め記事
いまさらアルゴリズムを学ぶ意味
コーディングに役立つ! アルゴリズムの基本(1)
 コンピュータに「3の倍数と3の付く数字」を判断させるにはどうしたらいいか。発想力を鍛えよう
Zope 3の魅力に迫る
Zope 3とは何ぞや?(1)
 Pythonで書かれたWebアプリケーションフレームワーク「Zope 3」。ほかのソフトウェアとは一体何が違っているのか?
貧弱環境プログラミングのススメ
柴田 淳のコーディング天国
 高性能なIT機器に囲まれた環境でコンピュータの動作原理に触れることは可能だろうか。貧弱なPC上にビットマップの直線をどうやって引く?
Haskellプログラミングの楽しみ方
のんびりHaskell(1)
 関数型言語に分類されるHaskell。C言語などの手続き型言語とまったく異なるプログラミングの世界に踏み出してみよう
ちょっと変わったLisp入門
Gaucheでメタプログラミング(1)
 Lispの一種であるScheme。いくつかある処理系の中でも気軽にスクリプトを書けるGaucheでLispの世界を体験してみよう
  Coding Edgeフォーラムフィード  2.01.00.91


Coding Edge フォーラム 新着記事
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

>

Coding Edge 記事ランキング

本日 月間