第3回 もう少し関数の話をしよう
山下 伸夫
株式会社タイムインターメディア
2009/1/14
関数型言語に分類されるHaskell。C言語などの手続き型言語とまったく異なるプログラミングの世界に踏み出してみよう(編集部)
第2回「関数の話をしよう」で関数を導入したときに新しい概念をたくさん紹介した。新しい概念が羅列されていて、少しペースを乱されたと思った方も多かったようだ。
新しい考え方あるいは用語というものは、通常「よく分からない」ものだ。それは、いままでに知らないことだからだし、だからこそ「新しい」考え方なのだから。
というのは筆者の言い訳にすぎない。説明を急ぎ過ぎたし、説明不足だったところもある。前回のおさらいを兼ねて関数についてもう少し説明する。
関数の型
前回定義した関数bmiの型を、もう一度確認してみよう。ghciの:typeコマンドを使えば、式の型を確認できる。まず、ghciを起動して、前回作成したBMI.hsをロードする。
% ghci |
次に、:typeコマンド使ってbmiの型を調べよう。
*BMI> :type bmi |
型シグネチャにある矢印->が、bmiが関数であることを示している。これは、->の左側にある型(この場合は(Height, Weight))の値を->の右側にある型(この場合はBMI)の値と対応付けるという意味である。このシグネチャは結局「bmiは(Height, Weight)の値をBMIの値に対応付ける」という意味である。
今度は関数bmicを見よう。bmicの型シグネチャには->が2つ含まれている。
*BMI> :type bmic |
最初に現れる->は、bmicに与える2つの引数らしきものを分離し、次に(最後に)現れる->は引数と返り値の境界を表しているように見える。このような見方をしてしまうと、->には2つの意味があるように思えて混乱の原因になるかもしれない。
型シグネチャ中では->は、型をオペランド(被演算子)とするある種の二項演算子と見なせる。もしそうなら、
Height -> Weight -> BMI |
はどのように解釈するのだろうか。
(Height -> Weight) -> BMI |
という解釈と、
Height -> (Weight -> BMI) |
という2通りの解釈があり得る。
そもそもbmicは、特定の身長の人専用にBMI値を「求める関数を作る関数」を作りたかったのである。従って、bmicの型は後者と解釈されなければ困る。実際に、後者のように解釈されているので安心してよい。
ある種の二項演算子と見なしたとき、->は右結合性を持っているのである。むしろ、->は右結合性を持っているので、a -> (b -> c)という型シグネチャの括弧を省略してa -> b -> cと書いても解釈にぶれはない。
実際、Haskellのプログラミングでは、a -> (b -> c)は、通常、括弧を省略して、a -> b -> cと書く。しかし、慣れないうちは意識して、a -> b -> cをa -> (b -> c)のように括弧を補って書いたり、考えたりするとよい。
関数適用
さて、bmicの型Height -> (Weight -> BMI)は、Heightと(Weight -> BMI)が->で結び付けられているので、「Heightという型の値から(Weight -> BMI)という型の値へ対応を付ける」という意味である。すなわち、bmicは、具体的なHeight型の値を指定すると、対応する(Weight -> BMI)型の値を示す関数、ということである。
関数に対して具体的な値を指定することを「関数を値に適用する」あるいは関数適用という。Haskellで関数適用を行うには、関数(の値を表す式)の右側に指定する具体的な値(を表す式)を置く。
では、bmicを1.71に適用すると、どのような型の値になるだろうか。ghciの:typeコマンドを使って調べてみよう。
*BMI> :type bmic 1.71 |
期待どおり、bmic 1.71の型は、Weight -> BMIである。ここでWeight -> BMIという型は、やはり、->で構成されているので、これは「Weightという型の値からBMIという型の値へ対応を付ける」関数であることを示している。
ということは、bmic 1.71という式が表す値はWeightからBMIへの関数である。従って、この値を具体的なWeight型の値に適用することができるはずであり、さらにその適用結果の値の型はBMIとなるはずである。実際にやってみると、
*BMI> :type (bmi 1.71) 79.5 |
期待どおりである。関数適用を表現するには、関数(の値を表す式)の右側に指定する具体的な値(を表す式)を置くのであるが、このとき「指定する具体的な値(を表す式)」を「(実)引数」という。1つの関数適用には、関数が1つ、引数が1つである。
関数適用を表す式では、関数と引数との間に目に見えない二項演算子があると見なせる。この目に見えない二項演算子の被演算子は関数と引数である。Haskellでは、この目に見えない関数適用を表す二項演算子は左結合性を持つ。従って、先ほどの(bmi 1.71) 79.5は括弧を省略しても解釈にあいまいさは生じない。
f g h |
とあれば、これは、
f (g h) |
とは違う。
(f g) h |
と同じなのである。bmicで実際に試してみよう。
*BMI> bmic 1.71 79.5
|
通常Haskellでは(f g) hは括弧を省略して、f g hと書く。このような括弧の省略は、1つの関数適用で複数の引数があるような錯覚を起こす。1つの関数適用には1つの引数である。慣れないうちは括弧を書いたり考えたりするとその意味がはっきり意識できる。
1/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を紹介する。※ショートカットキー、アクセスキーの解説あり
|
|