ウィキペディアとグーグルをチェックしましたが、ALGOL60で名前渡しがどのように機能するかについてはまだ頭を悩ませることができません。
8 に答える
Pass-By-NameParameterPassingで良い説明を見つけました。基本的に、関数の本体は、実際のパラメーターを関数の本体にテキストで置き換えた後、呼び出し時に解釈されます。この意味で、評価方法はCプリプロセッサマクロの評価方法と似ています。
実際のパラメーターを関数本体に置き換えることにより、関数本体は指定されたパラメーターの読み取りと書き込みの両方を行うことができます。この意味で、評価方法は参照渡しに似ています。違いは、名前渡しではパラメーターが関数内で評価されるため、などのパラメーターは、関数が呼び出される前の値を参照するのではなく、関数内のa[i]
現在の値に依存することです。i
a[i]
上でリンクしたページには、名前によるパスが有用で危険な場合の例がいくつかあります。名前による受け渡しによって可能になった手法は、今日では、参照による受け渡しやラムダ関数などの他のより安全な手法に大幅に取って代わられています。
ALGOL60では名前による呼び出しを意味していると思います。
名前による呼び出しは、渡されたパラメーターの値を変更できるという点で、参照による呼び出しに似ています。プロシージャが呼び出される前にパラメータが評価されず、代わりに遅延評価されるという点で、参照による呼び出しとは異なります。つまり、パラメータが実際に使用された場合にのみ評価されます。
たとえば、プロシージャがf(x, y)
あり、それを渡しi
、i/2
wherei
が最初はに等しいとし10
ます。f
に設定x
して42
から評価すると、値y
が表示されます21
(一方、参照による呼び出しまたは値による呼び出しでは、引き続き値が表示されます5
)。これは、式i/2
が評価されるまでy
評価されないためです。
多くの点で、これはパラメータのリテラルテキスト置換のように動作するように見えます(名前の競合を避けるために名前を変更します)。ただし、実際には、これは、渡された式に「サンク」(基本的にはクロージャ)を使用して実装されます。
ジェンセンのデバイスに関するウィキペディアの記事は、名前による呼び出しを使用するいくつかの興味深い例を示しています。そのうちの1つは次のとおりです。
real procedure Sum(k, l, u, ak) value l, u; integer k, l, u; real ak; comment k and ak are passed by name; begin real s; s := 0; for k := l step 1 until u do s := s + ak; Sum := s end;
この手順では、インデックス変数
k
と合計項ak
が名前で渡されます。名前による呼び出しにより、プロシージャはforループの実行中にインデックス変数の値を変更できます。名前で呼び出すak
と、ループの各反復中に引数が再評価されます。通常、ak
変更(副作用)に依存しますk
。たとえば、実数配列の最初の100項の合計を計算するコードは次の
V[]
ようになります。Sum(i, 1, 100, V[i]).
将来の人のために:
JohnC.Mitchellによるプログラミング言語の概念も役に立ちました。
名前によるパス。振り返ってみると、おそらくAlgol 60の最も奇妙な機能は、名前によるパスの使用です。名前渡しでは、プロシージャ呼び出しの結果は、仮パラメータがプロシージャの本体に置き換えられた場合と同じです。プロシージャをコピーし、正式なパラメータを置き換えることによってプロシージャ呼び出しの結果を定義するためのこのルールは、Algol60コピールールと呼ばれます。ラムダ計算のβ減少によって示されるように、コピールールは純粋な関数型プログラムではうまく機能しますが、正式なパラメーターに対する副作用との相互作用は少し奇妙です。これは、ジェンセンのデバイスと呼ばれる手法を示すプログラムの例です。式とそれに含まれる変数をプロシージャに渡して、プロシージャが1つのパラメータを使用して、他のパラメータが参照する場所を変更できるようにします。
begin integer i; integer procedure sum(i, j); integer i, j; comment parameters passed by name; begin integer sm; sm := 0; for i := 1 step 1 until 100 do sm := sm + j; sum := sm end; print(sum(i, i*10 )) end
このプログラムでは、プロシージャsum(i、j)は、iが1から100になるときに、jの値を合計します。コードを見ると、iを変更すると、 jの値; それ以外の場合、プロシージャは100*jを計算するだけです。ここに示されている呼び出しsum(i、i * 10)では、プロシージャsumの本体のforループは、iが1から100になると、i*10の値を合計します。
実際、名前による呼びかけは、単なる歴史的な好奇心ではありません。Windowsバッチファイル(および他の無数のスクリプト言語)で名前による呼び出しを行うことができます。それがどのように機能するか、そしてプログラミングでそれを効果的に使用する方法を知ることは、問題に対するきちんとした解決策を開くことができます。後で拡張するために文字列を渡すだけであることは知っていますが、名前による呼び出しと同様の効果を持つように操作できます。
call :assign x 1
exit /b
:assign
setlocal enabledelayedexpansion
(endlocal
:: Argument 1 is the name of the variable
set %1=%2
)
exit /b
Flatlanderには、Scalaでどのように機能するかを示す例があります。次の間に実装したいとします。
def mywhile(condition: => Boolean)(body: => Unit): Unit = if (condition) { body mywhile(condition)(body) }
これは次のように呼ぶことができます。
var i = 0 mywhile (i < 10) { println(i) i += 1 }
ScalaはAlgol60ではありませんが、おそらくそれはいくつかの光を当てます。
変数の記号形式で「名前」を渡すことができます。これにより、変数の更新とアクセスの両方を同時に行うことができます。例として、int型の変数xを3倍にしたいとします。
start double(x);
real x;
begin
x : = x * 3
end;
ALGOLは数学的アルゴリズムのために設計されました。名前による呼び出しの例として、合計関数が好きです。
申し訳ありませんが、私のALGOLは少し錆びていますが、構文が正しくない可能性があります。
.FUNCTION SUM(var,from,to,function)
.BEGIN
.REAL sum =0;
.FOR var = from .TO to .DO sum = sum + function;
return sum;
.END
あなたは次のような合計を使用することができます
Y = sum(x,1,4,sum(y,3,8,x+y));
上記では、内側のsum(y、3,8、x + y)は、名前のない関数を生成して、外側のsum呼び出しに渡します。変数xとyは、値ではなく名前で渡されます。変数の場合、名前による呼び出しは、Cのアドレス参照による呼び出しと同等です。再帰が含まれる場合、少し混乱します。
借り手はALGOLマシンを作りました。それらは3つのフラグビットを備えた48ビットのワードメモリを持っていました。フラグビットは、ALGOLの名前でcalを実装しました。これはスタックマシンであったため、関数がスタックにロードされると、fagという名前の呼び出しによって呼び出されます。式が引数として使用された場合、コンパイラは無名関数を生成します。変数は単純な間接参照になります。関数への書き込み中にエラーが発生します。
私はクラブに遅れて参加していることを知っています。これは必ずしも答えではありませんが、少し明確にするのに役立つことが1つ追加したいと思いました。私は常に、Algolの名前渡しを、C ++プリプロセッサディレクティブ(具体的にはマクロ)がコンパイル時に関数/変数の名前を実際のコードに置き換える場合と同様のプロセスだと考えてきました。名前による受け渡しは、基本的に仮パラメーターの名前を実際のパラメーターに置き換えて実行します。私はAlgolで書いたことがありませんが、名前によるパスはC++の参照によるパスと同じ結果になると聞いています。