web-dev-qa-db-ja.com

「名前渡し」とは何ですか?それは正確にどのように機能しますか?

私はウィキペディアとグーグルをチェックしましたが、ALGOL 60で名前渡しがどのように機能するかについてはまだ心に留めることができません。

35
bdd

Pass-By-Name Parameter Passing で良い説明を見つけました。基本的に、関数の本体は、実際のパラメーターを関数本体にテキストで置き換えた後、呼び出し時に解釈されます。この意味で、評価方法はCプリプロセッサマクロと同様です。

関数本体に実際のパラメーターを代入することにより、関数本体は指定されたパラメーターの読み取りと書き込みの両方を行うことができます。この意味で、評価方法は参照渡しと似ています。違いは、名前渡しでは、パラメーターが関数内でevaluatedであるため、a[i]などのパラメーターは現在の関数が呼び出される前のa[i]の値を参照するのではなく、関数内のiの値。

上でリンクしたページには、名前渡しが便利で危険な場合の例がいくつかあります。名前渡しによって可能になった技法は、今日では、参照渡し関数やラムダ関数などの他のより安全な技法に大きく取って代わられています。

37
Greg Hewgill

ALGOL 60で名前による呼び出しを意味していると思います。

名前による呼び出しは、渡されたパラメーターの値を変更できるという点で参照による呼び出しと似ています。パラメータがではなくプロシージャが呼び出される前に評価されるが、代わりに遅延評価されるという点で、参照による呼び出しとは異なります。つまり、パラメーターが実際に使用されたときにのみ評価されます。

たとえば、プロシージャf(x, y)があり、それをiおよびi/2に渡したとします。ここで、iは、最初は10と同じです。 fx42に設定し、次にyを評価すると、値21が表示されます(参照による呼び出しまたは値による呼び出しでは)それでも5が表示されます)。これは、yが評価されるまで、式i/2が評価されないためです。

多くの点で、これはパラメーターのリテラルテキスト置換のように動作するように見えます(名前の競合を回避するために名前を変更します)。ただし実際には、これは、渡された式に対して「サンク」(基本的にはクロージャ)を使用して実装されます。

Jensen's Device に関するWikipediaの記事には、名前による呼び出しの使用に関する興味深い例がいくつか示されています。以下はその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に依存します。

たとえば、実際の配列V[]の最初の100項の合計を計算するコードは次のようになります。

Sum(i, 1, 100, V[i]).
16

実際、名前による呼びかけは、単なる歴史的な好奇心ではありません。 Windowsバッチファイル(およびその他の無数のスクリプト言語)で名前による呼び出しを行うことができます。それがどのように機能するか、そしてプログラミングでそれを効果的に使用する方法を知ることは、問題に対するきちんとした解決策を開くことができます。私はそれが後の拡張のために文字列を渡すだけであることを知っていますが、名前による呼び出しと同様の効果を持つように操作することができます。

call :assign x 1
exit /b
:assign
setlocal enabledelayedexpansion
(endlocal
:: Argument 1 is the name of the variable
set %1=%2
)
exit /b

将来的には:

John C. Mitchellによるプログラミング言語の概念も役に立ちました。

Pass-by-Name。おそらく、振り返ってみると、ALGOL 60の最も奇妙な機能は、名前渡しの使用です。名前渡しでは、プロシージャー呼び出しの結果は、仮パラメーターがプロシージャーの本体に置き換えられた場合と同じです。プロシージャをコピーして仮パラメータを置き換えることによってプロシージャコールの結果を定義するこのルールは、ALGOL 60コピールールと呼ばれます。ラムダ計算のβ削減で示されているように、コピールールは純粋な関数型プログラムでうまく機能しますが、仮パラメーターへの副作用との相互作用は少し奇妙です。次に、Jensenのデバイスと呼ばれる手法を示すプログラム例を示します。式とそれに含まれる変数をプロシージャに渡し、プロシージャが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の値を加算します。

3
bdd

私はクラブに遅れて参加していることを知っていますが、これは必ずしも答えであるとは限りませんが、少し明確にするのに役立つものを1つ追加したいと思いました。 ALGOLの名前渡しは、C++プリプロセッサディレクティブ(具体的にはマクロ)がコンパイル時に関数/変数の名前を実際のコードに置き換える場合と同様のプロセスであると常に考えていました。名前渡しは、基本的に仮パラメーターの名前を実際のパラメーターに置き換えて実行します。 ALGOLで記述したことはありませんが、名前渡しがC++の参照渡しと同じ結果になると聞きました。

1
nhershy

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
G K

変数のシンボリック形式で「名前」を渡すと、変数の更新とアクセスの両方を同時に行うことができます。例として、int型の変数xを3倍にしたいとします。

start double(x);
real x;
begin
x : = x * 3
end;
1
Tawanda Moyo

Flatlanderの動作例は、Scala here でわかりやすく説明されています。whileを実装したいとします。

def mywhile(condition: => Boolean)(body: => Unit): Unit =
  if (condition) {
    body
    mywhile(condition)(body)
  }

これは次のように呼び出すことができます。

var i = 0
mywhile (i < 10) {
  println(i)
  i += 1
}

ScalaはALGOL 60ではありませんが、いくつかの光を放ちます。

1