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

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

2009/1/14

関数型言語に分類されるHaskell。C言語などの手続き型言語とまったく異なるプログラミングの世界に踏み出してみよう(編集部)

 第2回「関数の話をしよう」で関数を導入したときに新しい概念をたくさん紹介した。新しい概念が羅列されていて、少しペースを乱されたと思った方も多かったようだ。

 新しい考え方あるいは用語というものは、通常「よく分からない」ものだ。それは、いままでに知らないことだからだし、だからこそ「新しい」考え方なのだから。

 というのは筆者の言い訳にすぎない。説明を急ぎ過ぎたし、説明不足だったところもある。前回のおさらいを兼ねて関数についてもう少し説明する。

関数の型

 前回定義した関数bmiの型を、もう一度確認してみよう。ghciの:typeコマンドを使えば、式の型を確認できる。まず、ghciを起動して、前回作成したBMI.hsをロードする。

% ghci
GHCi, version 6.10.1: http://www.haskell.org/ghc/  :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer ... linking ... done.
Loading package base ... linking ... done.
Prelude> :load BMI.hs
[1 of 1] Compiling BMI              ( BMI.hs, interpreted )
Ok, modules loaded: BMI.
*BMI> _

 次に、:typeコマンド使ってbmiの型を調べよう。

*BMI> :type bmi
bmi :: (Height, Weight) -> BMI

 型シグネチャにある矢印->が、bmiが関数であることを示している。これは、->の左側にある型(この場合は(Height, Weight))の値を->の右側にある型(この場合はBMI)の値と対応付けるという意味である。このシグネチャは結局「bmiは(Height, Weight)の値をBMIの値に対応付ける」という意味である。

 今度は関数bmicを見よう。bmicの型シグネチャには->が2つ含まれている。

*BMI> :type bmic
bmic :: Height -> Weight -> BMI

 最初に現れる->は、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

 期待どおり、bmic 1.71の型は、Weight -> BMIである。ここでWeight -> BMIという型は、やはり、->で構成されているので、これは「Weightという型の値からBMIという型の値へ対応を付ける」関数であることを示している。

 ということは、bmic 1.71という式が表す値はWeightからBMIへの関数である。従って、この値を具体的なWeight型の値に適用することができるはずであり、さらにその適用結果の値の型はBMIとなるはずである。実際にやってみると、

*BMI> :type (bmi 1.71) 79.5
(bmic 1.71) 79.5 :: BMI

期待どおりである。関数適用を表現するには、関数(の値を表す式)の右側に指定する具体的な値(を表す式)を置くのであるが、このとき「指定する具体的な値(を表す式)」を「(実)引数」という。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
27.18785267261722
*BMI> (bmic 1.71) 79.5
27.18785267261722

 通常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の世界を体験してみよう
  Coding Edgeフォーラムフィード  2.01.00.91


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

注目のテーマ

>

Coding Edge 記事ランキング

本日 月間