コンテンツにスキップ

「評価戦略」の版間の差分

出典: フリー百科事典『ウィキペディア(Wikipedia)』
削除された内容 追加された内容
編集の要約なし
m 曖昧さ回避ページ演算へのリンクを解消、リンク先を演算子 (コンピュータ言語)に変更; リンク先を演算子 (コンピュータ言語)に変更; リンク先を演算子 (コンピュータ言語)に変更(DisamAssist使用)
 
(17人の利用者による、間の20版が非表示)
1行目:
{{参照方法|date=2014年9月}}
''''''{{lang-en-short|evaluation strategy}}[[]][[]][[ ()|]][[]]''''''[[ ()|]]
 
== 概要 ==
[[]][[|]][[]][[ ()|]][[]]
 
[[]]reduction(1) (2) (X Y) XY使[[|]]
8 ⟶ 9行目:
[[#|]] (strict) [[#|]] (non-strict) 
 
プログラミング言語によっては、複数の評価戦略を場合により選べるものもある。例えば[[C++]]は値呼びが基本だが、そのように参照呼びを指定することで参照呼びにもできる。
 
[[|]][[|]][[if]][[|]][[]]
15 ⟶ 16行目:
正格な評価とは、[[サブルーチン|関数]]の引数が常にその関数に引き渡される前に完全に評価されることを意味する。
 
[[{{ill2|]]|en|Church encoding}}[[ ()|]][[]]
 
=== 作用的順序 ===
<ref name="sicp">SICP[[]]</ref>applicative-order evaluation[[]][[]]使applynormal-order evaluationWikipedia[[ ()|]]
 
=== 値呼び ===
call by value: pass by value''''''
 
[[|]][[|]][[ ()|]]
 
=== 参照呼び ===
call by reference: pass by reference[[:en:Aliasing (computing)|Aliasing]][[Pascal]]
 
=== 参照の値渡し ===
仮引数の変数は実引数のエイリアスであるから、それへの代入は呼び出した側の'''変数'''にも影響する。これを、[[イミュータブル|ミュータブル]]なオブジェクトがある多くの言語において、オブジェクトへの「参照の'''値渡し'''」を行い、オブジェクトを変化させた場合に、呼び出した側から見て'''オブジェクト'''の変化が見えることと'''大変しばしば混同される。'''
参照の値渡し (共有呼び: call by sharing、オブジェクト呼び: call by object、call by object-sharing) は [[:en:Barbara Liskov|Barbara Liskov]] 他によって1974年に[[CLU]]言語で最初に call by sharing と呼ばれた評価戦略である。<ref name="CLU Reference Manual">[http://www.lcs.mit.edu/publications/pubs/pdf/MIT-LCS-TR-225.pdf CLU Reference Manual]</ref> 日本語では参照の値渡し<ref>[https://magazine.rubyist.net/articles/0032/0032-CallByValueAndCallByReference.html 値渡しと参照渡しの違いを理解する]</ref>とも呼ばれる。[[Python]]、Iota、[[Java]]、[[Ruby]]、[[JavaScript]]、[[Scheme]]、[[OCaml]]、[[AppleScript]]等の多数の言語で使われている。しかしながら「参照の値渡し」という用語は一般的では無く、異なる情報源間で用語の混乱が見られる。例えば、Javaの分野ではJavaは全て値渡しであると言われている。参照の値渡しは、言語上の値が[[プリミティブ型]]ではなく[[オブジェクト_(プログラミング)|オブジェクト]]に基づいているということ、つまり全ての値が「[[ボックス化]]」されていることを意味している。
 
参照の値渡しの意味論は参照渡しとは異なる: "In particular it is not call by value because mutations of arguments performed by the called routine will be visible to the caller. And it is not call by reference because access is not given to the variables of the caller, but merely to certain objects"{{Sfnp|CLU Reference Manual|1974|p=14-15}}(意訳「とりわけ、呼び出し先で行われた引数に対する変更は呼び出し元からも見えるため、値渡しではない。また、アクセスは呼び出し元の変数ではなく、単にあるオブジェクトに対して行われるため、参照渡しでもない。」) だから、例えば、変数が渡されたとき、呼び出し先のスコープ内で変数への代入を装うことは不可能である。ただし、関数は呼び出し元と同じオブジェクト(コピーされたものではない)にアクセスできるため、オブジェクトが可変([[ミュータブル]])であれば、関数内でのオブジェクトへの変更は呼び出し元にも反映される。これは値渡しの意味論とは異なる動作である。オブジェクトはコピーでもクローンでもない、つまり共有されているから、関数内での可変オブジェクトへの変更は、呼び出し元からも見えると言うことである。例として、配列(Array)が可変であるRubyで書くと:
関数の引数として参照を値渡しすることを参照渡しと呼ぶこともある、とか、[[C言語]]のように[[ポインタ (プログラミング)|ポインタ]]を持つ言語では、参照呼びではなく「アドレス呼び(call by address)」と呼ぶこともある、とか考える者もいるようだが、間違いである。C++ではなくC、Java、JavaScriptなどの値呼びしかない言語には参照呼びは無く、「それらの言語において似たように見えるもの」を「参照呼び」と呼ぶのは間違いである。間違った説明をネットに掲載したり、さらには書籍にまでする者が後を絶たず、非常に広範に流布しているが間違いである。C言語では、アドレス演算子 & により変数のアドレスを渡すことができるので、呼び先で元の変数の中身を変えてしまうことができるが、値呼びであることに変わりはない。
<syntaxhighlight lang="ruby">
def f(arr)
arr.append(1)
end
 
m = []
f(m)
p m
</syntaxhighlight>
これは <code>[1]</code> を出力する。なぜなら、<code>append</code>メソッドは呼び出されたオブジェクトを変更しているからだ。
 
これらの言語では、変数を渡すことは、変数によって参照される実際のオブジェクト渡す(アクセスする)事を意味しており、オリジナルの(呼び出し元の)変数にアクセする事では意味しているわけでは無いため、関数内での代入は呼び出し元に影響を与えない。再束縛された変数(仮引数となる変数)は関数内にしか存在しないため、呼び出しと元の対応する変数(実引数となる変数)は元の束縛を維持する。上記のRubyでの変更の動作と新しいオブジェクトを引数に代入する次のコードを比較してみると:
<syntaxhighlight lang="ruby">
def f(arr)
arr = [1]
end
 
m = []
f(m)
p m
</syntaxhighlight>
これは <code>[]</code> を出力する。なぜなら、<code>arr = [1]</code> という式は、新しい配列を、変数が参照する(つまりはオブジェクトの実体が保存されている)場所ではなく、'''変数'''そのものに再代入するからだ。
 
不変([[イミュータブル]])オブジェクトでは、オブジェクトの識別が変数そのものである言語を除き、参照の値渡しと値渡しに実質的な違いはない。可変オブジェクトでの参照の値渡しは、入出引数(input/output parameters)に代わるものである: 仮引数は代入せず(引数は上書きせず、オブジェクトの識別は変更しない)に、オブジェクト(引数)を変更する。
 
この用語は海外のPythonコミュニティ(call by sharingとして)および日本のRubyコミュニティ(参照の値渡しとして)では広く使用されているが、JavaやVisual Basic等の他の言語では同一の意味論でも値渡しとし、この場合の値はオブジェクトへの参照であると記述される場合が多い。
 
=== Call by copy-restore ===
39 ⟶ 67行目:
他にも、再帰呼び出しを行ったり、マルチスレッド環境で他のスレッドから観察されたりした場合には結果が異なってくる場合がある。
 
[[RPC遠隔手続き呼出し]] (RPC) などで、このようなふるまいが見られることがある。
 
=== 部分評価 ===
51 ⟶ 79行目:
 
=== 正規順序 ===
<ref>SICP< name="sicp"/ref>normal-order evaluation[[]][[]]使Wikipedia
 
=== 名前呼び ===
60 ⟶ 88行目:
[[ALGOL|ALGOL 60]] 
 
副作用のある言語における名前呼びには微妙なところがあり、[[:en:Jensen's Device|Jensen's Device]] のような例が知られている。
 
=== 必要呼び ===
70 ⟶ 98行目:
 
=== マクロ展開呼び ===
call by macro expansion使使{{|Hygienic[[|en|Hygienic macro}}]]
 
== 非決定性の戦略 ==
=== 部分適用 ===
部分適用はどちらかというと[[カリー化]]や[[第一級関数]]と関連する。複数の引数を取る関数において、一部の引数だけ適用された関数を得ることである。これに対して、すべての引数を適用することを'''完全適用'''と呼ぶ。
例として、Haskellのような関数型言語で、与えられた数値を2倍する関数を部分適用で作ると
<syntaxhighlight lang="haskell">
multiply x y = x * y
twice x = multiply x 2
</syntaxhighlight>
となる。部分適用されているのはtwiceを定義している'multiply x 2'の部分である。multiply関数は本来なら
<syntaxhighlight lang="haskell">multiply 2 3</syntaxhighlight>
のようにして使用するものであるが、これに2だけ適用して新たな関数twiceを得るのが部分適用である。
部分適用の重要性は[[モジュール性]]を高めることである。奇数・偶数の判定といった単純なものから、高階関数を駆使した複雑なものまで作り出すことができる。とくに遅延評価の言語で利用するとその効果は大きい。一方で、関数に副作用があると、思いもよらない結果をもたらすかもしれない。
 
=== 完全β-簡約 ===
完全β-簡約(full β-reduction)においては、任意の時点で任意の関数適用が簡約される(関数の引数を捕獲回避置換を使った関数に置換する)。これは、適用されない関数の本体内でも行われる。
80 ⟶ 120行目:
 
=== 楽観的評価 ===
Optimistic EveluationEvaluation1調
 
== 注 ==
87 ⟶ 127行目:
== 関連項目 ==
* [[先行評価]] - [[遅延評価]] - [[部分評価]] - [[短絡評価]]
*[[{{ill2|β正規形]]|en|Beta normal form}}
*[[プログラミング言語の比較]]
*[[ラムダ計算]]
*[[引数]]
103 ⟶ 142行目:
 
{{DEFAULTSORT:ひようかせんりやく}}
[[Category:プログラミング言語評価戦略|*]]
*[[Category:プログラミング言語の比較概念]]
[[Category:ソフトウェア工学]]
 

2024年2月21日 (水) 14:08時点における最新版


: evaluation strategy

[]




reduction(1) (2) (X Y) XY使

 (strict)  (non-strict) 

C++

if

[]





[]


[1]applicative-order evaluation使applynormal-order evaluationWikipedia

[]


call by value: pass by value


[]


call by reference: pass by referenceAliasingPascal

[]


 (: call by sharing: call by objectcall by object-sharing)  Barbara Liskov 1974CLU call by sharing [2] [3]PythonIotaJavaRubyJavaScriptSchemeOCamlAppleScript使JavaJava

: "In particular it is not call by value because mutations of arguments performed by the called routine will be visible to the caller. And it is not call by reference because access is not given to the variables of the caller, but merely to certain objects"[4]() ()()(Array)Ruby:
def f(arr)
  arr.append(1)
end

m = []
f(m)
p m

これは [1] を出力する。なぜなら、appendメソッドは呼び出されたオブジェクトを変更しているからだ。

これらの言語では、変数を渡すことは、変数によって参照される実際のオブジェクト渡す(アクセスする)事を意味しており、オリジナルの(呼び出し元の)変数にアクセする事では意味しているわけでは無いため、関数内での代入は呼び出し元に影響を与えない。再束縛された変数(仮引数となる変数)は関数内にしか存在しないため、呼び出しと元の対応する変数(実引数となる変数)は元の束縛を維持する。上記のRubyでの変更の動作と新しいオブジェクトを引数に代入する次のコードを比較してみると:

def f(arr)
  arr = [1]
end

m = []
f(m)
p m

 [] arr = [1] ()

()(input/output parameters): ()()

Python(call by sharing)Ruby()使JavaVisual Basic

Call by copy-restore[]


Call by copy-restore

 call by copy-restore 1



 (RPC) 

部分評価[編集]



[]


使

使

[]


[1]normal-order evaluation使Wikipedia

[]


call by name: pass by nameCapture-Avoiding Substitution使使使

使使

ALGOL 60 

Jensen's Device 

[]


call by need pass by need使



Haskell

[]


call by macro expansion使使

[]

[]


Haskell2
multiply x y = x * y
twice x = multiply x 2

となる。部分適用されているのはtwiceを定義している'multiply x 2'の部分である。multiply関数は本来なら

multiply 2 3

使2twice 使

β-[]


β-full β-reduction使

[]


call by futureparallel call by name使使

[]


Optimistic Evaluation1調

[編集]

  1. ^ a b 訳は、計算機プログラムの構造と解釈より
  2. ^ CLU Reference Manual
  3. ^ 値渡しと参照渡しの違いを理解する
  4. ^ CLU Reference Manual (1974), p. 14-15.

関連項目[編集]

参考文献[編集]