関数束縛
bmicの定義本体をもう一度見てみよう。型シグネチャとの対応も併せて見てほしい。
bmic :: Height -> (Weight -> BMI ) |
等式の左辺の変数bmicは関数の名前として機能する。右辺はHeight型の値から(Weight -> BMI)型の値への対応を付ける関数の値(を表す式)である。上の定義方法は値に名前を付けるという直截(ちょくせつ)な方法である。この等式は、左辺の変数を右辺の値に束縛しているので、変数束縛ということがある。
変数の値が関数である場合、関数適用を結果の値に束縛するという形式の等式で関数名を定義することができる。
bmic :: Height -> (Weight -> BMI ) |
2つの関数適用を結果の値に束縛するという形式の定義も可能である。
bmic :: Height -> (Weight -> BMI) |
->の右結合性と関数適用の左結合性があるので、Haskellの慣例では括弧を省略する。
bmic :: Height -> Weight -> BMI |
このような関数適用を結果の値に束縛するという形式の等式のことを関数束縛という。関数束縛の等式の左辺で導入された名前のうち、引数の部分で導入された名前はその関数束縛の等式の右辺でのみ有効であることに注意すること。
また、繰り返すが上のbmicの関数束縛の等式の左辺における関数適用の形式では、関数適用が2つ、それぞれに引数が1つずつである。
上のbmicの関数束縛による定義をしている等式の右辺をじっと眺めてみよう。
bmi (h,w) |
この式には3つの名前(変数)がある。bmi、h、wである。このうち、hとwは関数束縛の等式の左辺、引数の位置に現れている。この位置に現れる変数は、λ変数といい、関数が具体的な値(を表す式)に適用されたときに初めて束縛される。実際ghciで字面どおり評価しようとするとエラーが起こる。
*BMI> bmi (h,w) |
これは、h、wという変数の束縛(定義)が有効範囲で見当たらないという趣旨のエラーメッセージである。bmiはBMIモジュールで定義されているので、このモジュールがロードされている状態なら、エラーにはならない。
逆にbmiを定義しているモジュールがロードされていない状態でなら、当然、bmiという変数の束縛(定義)が有効範囲にないという旨のエラーとなる。ghciでは、:moduleコマンドを使ってモジュールをアンロードすることもできる。
*BMI> :module - BMI |
プロンプトが*BMI> から、> に変化した。これは、BMIモジュール(とPreludeモジュール)が見えている環境から、何のモジュールも見えていない環境に変わったことを表している。
> bmi (h,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)という関数への対応を示す。型シグネチャに現れている型名は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の世界を体験してみよう |

- プログラムの実行はどのようにして行われるのか、Linuxカーネルのコードから探る (2017/7/20)
C言語の「Hello World!」プログラムで使われる、「printf()」「main()」関数の中身を、デバッガによる解析と逆アセンブル、ソースコード読解などのさまざまな側面から探る連載。最終回は、Linuxカーネルの中では、プログラムの起動時にはどのような処理が行われているのかを探る - エンジニアならC言語プログラムの終わりに呼び出されるexit()の中身分かってますよね? (2017/7/13)
C言語の「Hello World!」プログラムで使われる、「printf()」「main()」関数の中身を、デバッガによる解析と逆アセンブル、ソースコード読解などのさまざまな側面から探る連載。今回は、プログラムの終わりに呼び出されるexit()の中身を探る - VBAにおけるFileDialog操作の基本&ドライブの空き容量、ファイルのサイズやタイムスタンプの取得方法 (2017/7/10)
指定したドライブの空き容量、ファイルのタイムスタンプや属性を取得する方法、FileDialog/エクスプローラー操作の基本を紹介します - さらば残業! 面倒くさいエクセル業務を楽にする「Excel VBA」とは (2017/7/6)
日頃発生する“面倒くさい業務”。簡単なプログラミングで効率化できる可能性がある。本稿では、業務で使うことが多い「Microsoft Excel」で使えるVBAを紹介する。※ショートカットキー、アクセスキーの解説あり
![]() |
|
|
|
![]() |