<< Prev | - Up - | Next >> |
Oz は関数記法を構文の利便性のために提供しています。私達は手続き呼び出しとして見た:
{P X1 ... Xn R}
これをネストした式の中で関数呼び出しとして使う事が出来ます:
{P X1 ... Xn}
Oz は手続きのための直接の構文記法として関数抽象も許します。それゆえ、以下の関数定義で:
fun {F X1 ... Xn}
SE
end
S は文で E は以下の関数定義に対応する式です:
proc {F X1 ... Xn R}
SR=
Eend
注意:手続き定義への変換も含めた関数のための厳密な構文は The Oz Notation Reference Manual に定義されています。
ここでは私達は読者の直観にたよります。大雑把に言うと、関数の構文の形式のための一般的なルールは手続きのそれと非常に似て見えます。その例外として、手続きでは制御のスレッドは文で終わり、対応する関数は式で終わるという事があります。
Figure 6.1 で示されるプログラムは Figure 5.7 で示されるプログラムと機能的に等しいものです。AndThen/2
がどの様に手続き AndThen/3
に展開されるかをよく見て下さい。下で、変換プロセスの直観的理解を与えるいくつものステップをお見せします。全ての中間の形式は正しい Oz のプログラムです。
fun {AndThen BP1 BP2}
if {BP1} then {BP2}
else false end
end
結果変数 B
を導入する事によって手続きを作ります:
proc {AndThen BP1 BP2 B}
B = if {BP1} then {BP2}
else false end
end
結果変数を外側の if-式 に移動して if-文 を作ります:
proc {AndThen BP1 BP2 B}
if {BP1} then B = {BP2}
else B = false end
end
% 構文変換: 関数記法
local
fun {AndThen BP1 BP2}
if {BP1} then {BP2}
else false end
end
fun {BinaryTree T}
case T
of nil then true
[] tree(K V T1 T2) then
{AndThen
fun {$} {BinaryTree T1} end
fun {$} {BinaryTree T2} end}
else false end
end
end
もしあなたが関数型プログラマーなら、元気を出して下さい。あなたは高階関数を含むあなた自身の関数を持てます、そして遅延評価の関数型言語と同様に、Oz は正格評価の関数型言語 1 Standard ML、Scheme、そして並行関数型言語 Erlang には無い末尾再帰の特定の形式を許します。しかしながら、Oz では標準の関数定義は遅延ではありません。遅延関数は Oz 2 においてもサポートされます。
ここで、よく知られた高階関数 Map/2
の例をお見せします。これは Standard ML や Scheme においては末尾再帰ではありませんが、Oz においては末尾再帰です。
fun {Map Xs F}
case Xs
of nil then nil
[] X|Xr then {F X}|{Map Xr F}
end
end
{Browse {Map [1 2 3 4] fun {$ X} X*X end}}
andthen
と orelse
結局、私達は多くの仕事を無料でしてきました!Oz は既に遅延(非正格)バージョンの Boolean 関数 And/2
と Or/2
をそれぞれ andthen
と orelse
として提供しています。前者は関数 AndThen/2
の様に振る舞い、後者は最初の引数が false
と評価された場合のみ評価されます。通常、これらの演算子はプリミティブではなく、Oz で定義されます。Figure 6.2 は関数 BinaryTree
の最終版です。
fun {BinaryTree T}
case T of nil then true
[] tree(K V T1 T2) then
{BinaryTree T1} andthen {BinaryTree T2}
else false end
end
今から、原則として、私達は手続きと関数の両方を使う事によっていくらかの構文的な重複を持っていますが、問いはいつ関数記法を使っていつ使わないのかです。誠実な回答は、「それはあなたの責任です」となります!私の個人的な意見を述べましょう。いくつかのルールの概要です:
最初に、私が好きではないものです。手続き P
を定義してそれを関数として呼び出さない事、つまり手続きのために関数ネストを使わない事。SMerge
の例の中での様に、ネストマーカーの代わりに手続きネストを使う事。さらに言うと、関数を定義したら、それを関数として呼ぶ事。
私は、対象が本当に関数的、すなわち、一つの出力、複数の入力、そして出力が入力引数の数学的関数となっているのであれば、関数定義を使うのを好みます。
私はその他の場合、すなわち、複数の出力、状態を持つデータ型の様な関数的でない定義、非決定的な定義であるその他多くの場合には手続きを使うのを好みます 3 。
厳密には関数的でない定義であっても情報の流れの明確な方向性があれば、前のルールを緩めて関数を使っても良いかもしれません。こうすれば関数は簡潔になります。
<< Prev | - Up - | Next >> |