「評価戦略」の版間の差分
とりあえず半分ぐらい、大幅に改修 |
残りもざっと |
||
8行目: | 8行目: | ||
評価戦略は、関数の引数をどう扱うかによって、[[#正格な評価|正格]] (strict) な評価戦略と[[#正格でない評価|非正格]] (non-strict) な評価戦略に大きく分類される。 |
評価戦略は、関数の引数をどう扱うかによって、[[#正格な評価|正格]] (strict) な評価戦略と[[#正格でない評価|非正格]] (non-strict) な評価戦略に大きく分類される。 |
||
プログラミング言語によっては、複数の評価戦略を場合により選べるものもある。例えば[[C++]]は、値 |
プログラミング言語によっては、複数の評価戦略を場合により選べるものもある。例えば[[C++]]は、値呼びが基本だが、そのように指定することで参照呼びにもできる。 |
||
正格評価の[[手続き型プログラミング|手続き型言語]]ないし[[命令型プログラミング|命令型言語]]における、[[if文]]のような制御の分岐をおこなう[[構造化プログラミング|構造]]は、そういった言語の中で一種の非正格評価を実現するものとみなせる。また[[短絡評価]]される演算子も同様にみなせる。
|
正格評価の[[手続き型プログラミング|手続き型言語]]ないし[[命令型プログラミング|命令型言語]]における、[[if文]]のような制御の分岐をおこなう[[構造化プログラミング|構造]]は、そういった言語の中で一種の非正格評価を実現するものとみなせる。また[[短絡評価]]される演算子も同様にみなせる。
|
||
18行目: | 18行目: | ||
=== 作用的順序 === |
=== 作用的順序 === |
||
作用的順序の評価<ref>訳は、和田によるSICPの訳書による</ref>(applicative-order evaluation)は、もっぱら[[プログラミング言語]]よりは[[計算模型]]で使われる用語で、まず引数を全て評価し、それを関数にapply(作用、ないし適用)する、という方法である。正規順序の評価(normal-order evaluation)の逆として、対になっている。プログラミング言語における値 |
作用的順序の評価<ref>訳は、和田によるSICPの訳書による</ref>(applicative-order evaluation)は、もっぱら[[プログラミング言語]]よりは[[計算模型]]で使われる用語で、まず引数を全て評価し、それを関数にapply(作用、ないし適用)する、という方法である。正規順序の評価(normal-order evaluation)の逆として、対になっている。プログラミング言語における値呼びに同じとされることも多いが、英語版Wikipediaでは、関数の引数を左から右に[[木構造 (データ構造)|後順]]に走査して簡約可能な式を簡約していく評価戦略で、値呼びとは異なり、適用順序評価は関数を実際に適用する以前に可能な限り関数本体内の項数を減らそうとするものとしている。 |
||
=== 値呼び === |
=== 値呼び === |
||
26行目: | 26行目: | ||
=== 参照呼び === |
=== 参照呼び === |
||
参照呼び︵call by reference、参照渡し: pass by reference︶では、仮引数が実引数そのもの、すなわちエイリアス︵[[:en:Aliasing (computing)]]︶になる |
参照呼び(call by reference、参照渡し: pass by reference)では、仮引数が実引数そのもの、すなわちエイリアス([[:en:Aliasing (computing)]])になる。実引数は、左辺値を持たねばならない([[Pascal]]のように、実引数を変数に限定した言語もある)か、左辺値を持たない式の場合は呼び出し側で一時的オブジェクトを構築する言語もある。 |
||
仮引数の変数は実引数のエイリアスであるから、それへの代入は呼び出した側の'''変数'''にも影響する。これを、[[イミュータブル|ミュータブル]]なオブジェクトがある多くの言語において、オブジェクトへの「参照の'''値渡し'''」を行い、オブジェクトを変化させた場合に、呼び出した側から見て'''オブジェクト'''の変化が見えることと'''大変しばしば混同される。''' |
仮引数の変数は実引数のエイリアスであるから、それへの代入は呼び出した側の'''変数'''にも影響する。これを、[[イミュータブル|ミュータブル]]なオブジェクトがある多くの言語において、オブジェクトへの「参照の'''値渡し'''」を行い、オブジェクトを変化させた場合に、呼び出した側から見て'''オブジェクト'''の変化が見えることと'''大変しばしば混同される。''' |
||
'''関数の引数として参照を値 |
'''関数の引数として参照を値呼びすることを参照呼びと呼ぶこともある、とか、[[C言語]]のように[[ポインタ (プログラミング)|ポインタ]]を持つ言語では、参照呼びではなく﹁アドレス呼び︵call byaddress︶﹂と呼ぶこともある、とか考える者もいるようだが、間違いである。C++ではなくC、Java、JavaScriptなどの値呼びしかない言語には参照呼びは無く、﹁それらの言語において似たように見えるもの﹂を﹁参照呼び﹂と呼ぶのは間違いである。間違った説明をネットに掲載したり、さらには書籍にまでする者が後を絶たず、非常に広範に流布しているが間違いである。'''C言語では、アドレス演算子 & により変数のアドレスを渡すことができるので、呼び先で元の変数の中身を変えてしまうことができるが、値呼びであることに変わりはない。
|
||
=== Call by copy-restore === |
=== Call by copy-restore === |
||
51行目: | 51行目: | ||
=== 正規順序 === |
=== 正規順序 === |
||
正規順序の評価<ref>訳は、和田によるSICPの訳書による</ref>(normal-order evaluation)も、もっぱら[[プログラミング言語]]よりは[[計算模型]]で使われる用語で、最も外側の簡約可能な式を簡約する評価戦略であり、関数の引数を評価する前に関数を適用する。プログラミング言語における名前 |
正規順序の評価<ref>訳は、和田によるSICPの訳書による</ref>(normal-order evaluation)も、もっぱら[[プログラミング言語]]よりは[[計算模型]]で使われる用語で、最も外側の簡約可能な式を簡約する評価戦略であり、関数の引数を評価する前に関数を適用する。プログラミング言語における名前呼びに同じとされることも多いが、英語版Wikipediaでは、名前呼びでは適用されない関数の本体内までは評価しない点が異なるとしている。 |
||
=== 名前 |
=== 名前呼び === |
||
名前渡し |
名前呼び(call by name、名前渡し: pass by name)では、関数の引数は全く評価されず、捕獲回避置換(Capture-Avoiding Substitution)を使って関数本体内に直接置換される。引数がその関数の評価で使われていない場合、その引数は全く評価されない。引数が複数回使われている場合、その度に再評価される。 |
||
値 |
値呼びはその引数が全く使われていなくとも必ず評価するため、名前呼びの方が好ましいとする考え方もある。しかし、引数が使われている場合は名前呼びの方が性能が悪いという批判もある。 |
||
名前 |
名前呼びはそのまま実装されることは滅多に無い。実際の言語では、名前呼びの意味論は次の必要呼び評価の実装となっていることが多い。[[ALGOL|ALGOL 60]] ではデフォルトの評価戦略が名前呼びである。
|
||
副作用のある言語における名前呼びには微妙なところがあり、[[:en:Jensen's Device]] のような例が知られている。 |
|||
⚫ | |||
⚫ | 必要渡し |
||
⚫ | |||
必要渡しを使っている言語では、[[モナド (プログラミング)|モナド]]などを使う場合以外では計算の副作用をサポートしない。これによって、遅延評価以前に変数の値が変更されることによる予期しない振る舞いを防ぐ。 |
|||
⚫ | 必要呼び(call by need、必要渡し pass by need)とは名前呼びを[[メモ化]]したようなもので、関数の引数が評価されると、その値がそれ以降の利用のために代替として格納される。副作用がない場合、これは名前呼びと同じ結果となる。引数が何度も使われている場合、(更新に要するコストの合計が毎回計算するコストの合計より小さければ)必要呼びの方が性能がよい。 |
||
|
[[遅延評価]]を引数の評価戦略として見た場合は必要呼びである、と言える。 |
||
必要 |
必要呼びの言語としては[[Haskell]]が有名である。 |
||
=== マクロ展開 |
=== マクロ展開呼び === |
||
マクロ展開 |
マクロ展開呼び︵call bymacro expansion︶は名前呼びと似ているが、捕獲回避置換ではなくテキスト置換を使う︵ため、捕獲が起き得る︶。マクロ展開呼びであることを意識せずに使っていると、予期しない結果になることがある。{{仮リンク|Hygienicマクロ|en|Hygienic macro}}は、捕獲を回避するマクロである。
|
||
== 非決定性の戦略 == |
== 非決定性の戦略 == |
||
76行目: | 76行目: | ||
完全β-簡約(full β-reduction)においては、任意の時点で任意の関数適用が簡約される(関数の引数を捕獲回避置換を使った関数に置換する)。これは、適用されない関数の本体内でも行われる。 |
完全β-簡約(full β-reduction)においては、任意の時点で任意の関数適用が簡約される(関数の引数を捕獲回避置換を使った関数に置換する)。これは、適用されない関数の本体内でも行われる。 |
||
=== 未来 |
=== 未来呼び === |
||
未来 |
未来呼び(call by future)あるいは並列名前呼び(parallel call by name)は必要呼びに似ているが、関数の引数は(必要に応じてではなく)関数本体と並行して(別スレッドで)評価される。関数本体で引数を使用するときにスレッドの同期が行われる。引数が全く使われない場合、引数の評価をしているスレッドは中断され捨てられる。 |
||
=== 楽観的評価 === |
=== 楽観的評価 === |
||
楽観的評価(Optimistic Eveluation)は必要 |
楽観的評価(Optimistic Eveluation)は必要呼びの変形の1つであり、関数の引数はある回数だけ部分評価される(回数は実行時に調整される)。そして、評価は中断され、必要呼びで関数が適用される。この方法では必要呼びの性能低下を防ぎつつ、停止属性を保持する。 |
||
== 注 == |
== 注 == |
2013年10月10日 (木) 03:40時点における版
概要
プログラミング言語では、その意味のうち、サブルーチン呼び出しや演算子式の評価において引数をいつどういう順序で評価し、仮引数は実引数にどう置換されるのか、サブルーチン呼び出しや演算子式の値への置換はどうなのかといったことが、言語仕様によって、あるいは実装によって定義される︵あるいは未定義とされる︶。 ラムダ計算︵など︶における評価すなわち簡約︵reduction︶においては﹁(1)入れ子状になった式の最も外側から簡約するか、最も内側から簡約するか (2)関数適用の (X Y) という形の式において、XとYのどちらの簡約を先にするか﹂という選択肢がある。これは後述する、プログラミング言語における正格評価と非正格評価にほぼ対応し、同じ言葉が使われることもあるが、︵プログラミング言語の意味論の議論といった場合を別にして︶モデルについての議論と実装についての議論であり、混同するのが望ましくない場合もある。 評価戦略は、関数の引数をどう扱うかによって、正格 (strict) な評価戦略と非正格 (non-strict) な評価戦略に大きく分類される。 プログラミング言語によっては、複数の評価戦略を場合により選べるものもある。例えばC++は、値呼びが基本だが、そのように指定することで参照呼びにもできる。 正格評価の手続き型言語ないし命令型言語における、if文のような制御の分岐をおこなう構造は、そういった言語の中で一種の非正格評価を実現するものとみなせる。また短絡評価される演算子も同様にみなせる。正格な評価
正格な評価とは、関数の引数が常にその関数に引き渡される前に完全に評価されることを意味する。 チャーチ符号化においては、演算子の先行評価は関数の正格な評価に写像される。そのため、正格な評価は﹁先行評価﹂とも呼ばれる。多くのプログラミング言語は、関数については正格な評価をする。作用的順序
作用的順序の評価[1]︵applicative-order evaluation︶は、もっぱらプログラミング言語よりは計算模型で使われる用語で、まず引数を全て評価し、それを関数にapply︵作用、ないし適用︶する、という方法である。正規順序の評価︵normal-order evaluation︶の逆として、対になっている。プログラミング言語における値呼びに同じとされることも多いが、英語版Wikipediaでは、関数の引数を左から右に後順に走査して簡約可能な式を簡約していく評価戦略で、値呼びとは異なり、適用順序評価は関数を実際に適用する以前に可能な限り関数本体内の項数を減らそうとするものとしている。値呼び
値呼び︵call by value、値渡し: pass by value︶は、多くの言語で採用されている典型的な評価戦略である。値呼びでは、関数呼び出しにある実引数を評価し、関数の仮引数を新しい変数としてその値に束縛し、しかる後に関数本体を実行する。関数の中で仮引数である変数に値を代入しても、それは局所的なコピーへの代入であり、呼び出した側の変数には影響しない。 手続き型言語ないし命令型言語では、演算子の左辺と右辺や、複数個並んだ引数の評価順が左から右であるか右から左であるかは、結果に違いを齎すことがあるが、仕様で決めている言語もあれば、決めていない言語もある。参照呼び
参照呼び︵call by reference、参照渡し: pass by reference︶では、仮引数が実引数そのもの、すなわちエイリアス︵en:Aliasing (computing)︶になる。実引数は、左辺値を持たねばならない︵Pascalのように、実引数を変数に限定した言語もある︶か、左辺値を持たない式の場合は呼び出し側で一時的オブジェクトを構築する言語もある。 仮引数の変数は実引数のエイリアスであるから、それへの代入は呼び出した側の変数にも影響する。これを、ミュータブルなオブジェクトがある多くの言語において、オブジェクトへの﹁参照の値渡し﹂を行い、オブジェクトを変化させた場合に、呼び出した側から見てオブジェクトの変化が見えることと大変しばしば混同される。 関数の引数として参照を値呼びすることを参照呼びと呼ぶこともある、とか、C言語のようにポインタを持つ言語では、参照呼びではなく﹁アドレス呼び︵call by address︶﹂と呼ぶこともある、とか考える者もいるようだが、間違いである。C++ではなくC、Java、JavaScriptなどの値呼びしかない言語には参照呼びは無く、﹁それらの言語において似たように見えるもの﹂を﹁参照呼び﹂と呼ぶのは間違いである。間違った説明をネットに掲載したり、さらには書籍にまでする者が後を絶たず、非常に広範に流布しているが間違いである。C言語では、アドレス演算子 & により変数のアドレスを渡すことができるので、呼び先で元の変数の中身を変えてしまうことができるが、値呼びであることに変わりはない。Call by copy-restore
Call by copy-restore︵複製呼びの結果返し、などと意訳される︶は、参照呼びの特殊な実装とも見ることができる。実引数の値が値呼びと同様にコピーされるが、関数呼び出しから戻る時に仮引数の変数の値が、あたかも参照呼びされたかのように書き戻される。 参照呼びと異なるのは、ある call by copy-restore の関数呼び出しの複数の引数に同じ変数を渡した場合、参照呼びでは、引数の1つを更新すると他の引数の内容も更新されるが、こちらでは、それぞれが異なるコピーであるため、他の引数の内容が更新されない。呼び出し側に戻ったときにどうなるかはそれぞれの仕様ないし実装による。 他にも、再帰呼び出しを行ったり、マルチスレッド環境で他のスレッドから観察されたりした場合には結果が異なってくる場合がある。 RPCなどで、このようなふるまいが見られることがある。部分評価
正格でない評価
正格でない評価では、関数の引数は関数本体の評価で実際に使われるまで評価されない。 チャーチ符号化においては、演算子の遅延評価は関数の正格でない評価に写像される。そのため、正格でない評価は﹁遅延評価﹂とも呼ばれる。ブーリアン型の式は多くの言語で遅延評価されるが、この場合は短絡評価と呼ぶことが多い。条件式でも違う理由で遅延評価が使われることが多い。正規順序
正規順序の評価[2]︵normal-order evaluation︶も、もっぱらプログラミング言語よりは計算模型で使われる用語で、最も外側の簡約可能な式を簡約する評価戦略であり、関数の引数を評価する前に関数を適用する。プログラミング言語における名前呼びに同じとされることも多いが、英語版Wikipediaでは、名前呼びでは適用されない関数の本体内までは評価しない点が異なるとしている。名前呼び
名前呼び︵call by name、名前渡し: pass by name︶では、関数の引数は全く評価されず、捕獲回避置換︵Capture-Avoiding Substitution︶を使って関数本体内に直接置換される。引数がその関数の評価で使われていない場合、その引数は全く評価されない。引数が複数回使われている場合、その度に再評価される。 値呼びはその引数が全く使われていなくとも必ず評価するため、名前呼びの方が好ましいとする考え方もある。しかし、引数が使われている場合は名前呼びの方が性能が悪いという批判もある。 名前呼びはそのまま実装されることは滅多に無い。実際の言語では、名前呼びの意味論は次の必要呼び評価の実装となっていることが多い。ALGOL 60 ではデフォルトの評価戦略が名前呼びである。 副作用のある言語における名前呼びには微妙なところがあり、en:Jensen's Device のような例が知られている。必要呼び
必要呼び︵call by need、必要渡し pass by need︶とは名前呼びをメモ化したようなもので、関数の引数が評価されると、その値がそれ以降の利用のために代替として格納される。副作用がない場合、これは名前呼びと同じ結果となる。引数が何度も使われている場合、︵更新に要するコストの合計が毎回計算するコストの合計より小さければ︶必要呼びの方が性能がよい。 遅延評価を引数の評価戦略として見た場合は必要呼びである、と言える。 必要呼びの言語としてはHaskellが有名である。マクロ展開呼び
マクロ展開呼び︵call by macro expansion︶は名前呼びと似ているが、捕獲回避置換ではなくテキスト置換を使う︵ため、捕獲が起き得る︶。マクロ展開呼びであることを意識せずに使っていると、予期しない結果になることがある。Hygienicマクロは、捕獲を回避するマクロである。非決定性の戦略
完全β-簡約
完全β-簡約︵full β-reduction︶においては、任意の時点で任意の関数適用が簡約される︵関数の引数を捕獲回避置換を使った関数に置換する︶。これは、適用されない関数の本体内でも行われる。未来呼び
未来呼び︵call by future︶あるいは並列名前呼び︵parallel call by name︶は必要呼びに似ているが、関数の引数は︵必要に応じてではなく︶関数本体と並行して︵別スレッドで︶評価される。関数本体で引数を使用するときにスレッドの同期が行われる。引数が全く使われない場合、引数の評価をしているスレッドは中断され捨てられる。楽観的評価
楽観的評価︵Optimistic Eveluation︶は必要呼びの変形の1つであり、関数の引数はある回数だけ部分評価される︵回数は実行時に調整される︶。そして、評価は中断され、必要呼びで関数が適用される。この方法では必要呼びの性能低下を防ぎつつ、停止属性を保持する。注
関連項目
参考文献
- Harold Abelson and Gerald Jay Sussman. Structure and Interpretation of Computer Programs, Second Edition. MIT Press, 1996. ISBN 0-262-01153-0.
- Henry G. Baker, Jr. "The Incremental Garbage Collection of Processes", with Carl Hewitt, ACM Sigplan Notices 12. August 8, 1977. Pages 55-59.
- Clem Baker-Finch, Clem, David King, Jon Hall, and Phil Trinder. "An Operational Semantics for Parallel Call-by-Need", Research report 99/1. Faculty of Mathematics & Computing, The Open University, 1999.
- Robert Ennals and Simon Peyton Jones. "Optimistic Evaluation: a fast evaluation strategy for non-strict programs", in ICFP'03. ACM Press, 2003.
- Jocelyn Frechot. "Partial Evaluation", documentation for the Compose project. Online, Sept. 25, 2003.
- Bertram Ludäscher. CSE 130 lecture notes. January 24, 2001.
- Benjamin C. Pierce. Types and Programming Languages. MIT Press, 2002. ISBN 0-262-16209-1.
- P. Sestoft. "Demonstrating Lambda Calculus Reduction", in T. Mogensen, D. Schmidt, I. H. Sudburough (editors): The Essence of Computation: Complexity, Analysis, Transformation. Essays Dedicated to Neil D. Jones. Lecture Notes in Computer Science 2566. Springer-Verlag, 2002. Pages 420-435. ISBN 3-540-00326-6