2008年6月24日火曜日

Haskell のセクションと中置記法

1. セクションの特徴は何?

Haskell の2項演算子に対して部分適用したものをセクションと呼ぶ。

ふつうのHaskellプログラミング によると、

部分適用を使い引数を1つだけ渡した二項演算子のことを特にセクション (section) と言い、普通の部分適用とは少し違った特殊な機能が用意されています。 (p205)

「部分適用とは少し違った特殊な機能」とはなんだろう?

部分適用については、以下を参照。

セクションとは、Haskell 98 Report: 式 によると、

セクション( op e ) あるいは ( e op ) のように書く。ここで、op は 二項演算子であり、e は式である。セクションは二項演算子の部分適 用をあらわすのに便利な構文である。

 

2. 二項演算子を関数として用いる

二項演算子とは、演算子 - Wikipedia によると、

二つのオペランドから一つの結果を得る演算を表す演算子を二項演算子という。数学での写像を表現するのに通常は前置記法で書くのに対して、二項演算子は中置記法で書くことが多い。つまり、"k + 3" のように演算子を二つのオペランドの中間に置く。

Haskell では、二項演算子を中置記法で書くことができる。

Haskell 98 Report: 式の「3.4 演算子適用」には、

e1 qop e2 という 形式は二項演算子 qop の式 e1 および e2 への中置適用である。(...)

変換:
以下の同一性が保存される:

e1 op e2 = (op) e1 e2

例えば、

Prelude> 1 + 2
3

Prelude> (+) 1 2
3

Prelude> :t (+)
(+) :: (Num a) => a -> a -> a

 + という二項演算子は、関数 (+) である。この関数に対して、部分適用をしてみる。

Prelude> :t (+) 1
(+) 1 :: (Num t) => t -> t

 

セクションの型を調べる

次に、+ 演算子をセクションとして用い、型を調べてみる。

Prelude> :t (1 +)
(1 +) :: (Num t) => t -> t

Prelude> :t (+ 1)
(+ 1) :: (Num a) => a -> a

 

部分適用する引数によって異なるセクション

二つのセクションの違いは、除算の場合で考えると分かりやすい。

Prelude> (/ 2) 4
2.0

Prelude> (2 /) 4
0.5

前者のセクションは 2 割る計算を表し、後者は 2 割る計算である。2つのセクションの違いは、部分適用するために渡す引数の位置、項が違うということ。

二項演算子の部分適用に対して、特別に「セクション」と名前が付けられているのは、部分適用で渡す引数の位置を指定できるため。

 

3. 関数を中置記法で書く

Haskell では、関数も二項演算子の中置記法のように書くことができる。

識別子を使って関数を定義した場合でも、関数名を「` `」で囲うと二項演算子として使えるようになります。例えば関数f を定義したら、`f` は二項演算子として使えます。

(ふつうのHaskellプログラミング, p181)

例えば、 hoge  x y のように引数を二つとる関数を定義した場合、 x `hoge` y と書ける。

Prelude> let hoge x y = x / y

Prelude> hoge 4 2
2.0
Prelude> 4 `hoge` 2
2.0

 

関数をセクションとして用いる

この関数に対して部分適用をし、セクションと同じ形式で関数を適用してみる。

Prelude> (hoge 2) 4
0.5

Prelude> (`hoge` 2) 4
2.0
Prelude> (2 `hoge`) 4
0.5
関数合成とセクションを組みわせた場合、次にように書くことができる。

Prelude> let piyo x = (+ x) . (/ 2)

-- 普通の関数の適用
Prelude> piyo 100 50
125.0

-- 部分適用を用いて
Prelude> (piyo 100) 50
125.0

-- セクションを用いて
Prelude> (100 `piyo`) 50
125.0
Prelude> (`piyo` 50) 100
125.0

ふぅ~、やっと見慣れてきた。 ^^;