簡単に言えば、コール ポイントでブロックを評価する必要は一般にないためです。Rebol のブロックはパラメーターをとらないため、どこで評価されるかはほとんど問題になりません。ただし、その「ほとんど」には説明が必要かもしれません...
それは、Rebol の 2 つの興味深い機能、つまり静的バインディングとdo
、関数の動作方法に帰着します。
静的バインディングとスコープ
Rebol にはスコープ付きの単語バインディングはなく、静的な直接の単語バインディングがあります。字句スコープがあるように見えることもありますが、新しい「スコープ」コード ブロックを構築するたびに静的バインディングを更新することで、実際にはそれを偽装しています。いつでも単語を手動で再バインドすることもできます。
この場合、これが意味することは、ブロックが存在すると、そのバインディングと値は静的であり、ブロックが物理的に配置されている場所や評価されている場所の影響を受けないということです。
ただし、ここが難しいところです。関数のコンテキストは奇妙です。関数コンテキストにバインドされた単語のバインディングは静的ですが、それらの単語に割り当てられた値のセットは動的にスコープされます。これは、Rebol でコードが評価される方法の副作用です。他の言語の言語ステートメントとは Rebol では関数であるため、if
たとえば への呼び出しは、実際にはデータのブロックをif
関数にif
渡し、次に に渡しdo
ます。つまり、関数の実行中に、do
まだ返されていない関数への最新の呼び出しの呼び出しフレームから、その単語の値を検索する必要があります。
これは、関数を呼び出して、単語がコンテキストにバインドされたコード ブロックを返す場合、関数が返された後にそのブロックの評価が失敗することを意味します。ただし、関数が自分自身を呼び出し、その呼び出しが単語にバインドされたコード ブロックを返す場合、関数が戻る前にそのブロックを評価すると、関数の現在の呼び出しの呼び出しフレームでそれらの単語を検索します。
これは、あなたであろうと であろうと同じでdo
ありreturn/redo
、内部関数にも影響します。実演してみましょう:
関数が戻った後に評価されるコードを返す関数は、関数語を参照します。
>> a: 10 do do has [a] [a: 20 [a]]
** Script error: a word is not bound to a context
** Where: do
** Near: do do has [a] [a: 20 [a]]
同じですがreturn/redo
、関数内のコード:
>> a: 10 do has [a] [a: 20 return/redo does [a]]
** Script error: a word is not bound to a context
** Where: function!
** Near: [a: 20 return/redo does [a]]
コードdo
バージョンですが、同じ関数への外部呼び出しの内部:
>> do f: function [x] [a: 10 either zero? x [do f 1] [a: 20 [a]]] 0
== 10
同じですがreturn/redo
、関数内のコード:
>> do f: function [x] [a: 10 either zero? x [f 1] [a: 20 return/redo does [a]]] 0
== 10
つまり、ブロックを使用すると、通常、ブロックが定義されている場所以外の場所でブロックを実行しても利点はありません。必要に応じて、do
代わりに別の呼び出しを使用する方が簡単です。同じ関数の外部呼び出しで実行されるコードを返す必要がある自己呼び出し再帰関数は、非常にまれなコード パターンであり、Rebol コードで使用されたことはまったくありません。
return/redo
ブロックも同様に処理するように変更することは可能かもしれませんがreturn/redo
、まれな状況でのみ役立つ機能を追加するためにオーバーヘッドが増加する価値はおそらくなく、すでにより良い方法がdo
あります。
ただし、これは興味深い点をもたらします。同じ仕事return/redo
をするのでブロックが必要ない場合、関数にも同じことが当てはまりますか? do
なぜ必要なreturn/redo
のですか?
関数の DO の仕組み
基本的に、関数のreturn/redo
実装に使用するのとまったく同じコードを使用するためです。do
気付いていないかもしれませんがdo
、関数は本当に珍しいものです。
関数値を呼び出すことができるほとんどのプログラミング言語では、R3 の関数がどのように機能するかのように、パラメータを完全なセットとして関数に渡す必要がありますapply
。通常の Rebol 関数の呼び出しでは、未知の事前評価規則を使用して、その引数に対して未知の事前評価の回数が追加で発生します。エバリュエーターは実行時にこれらの評価ルールを把握し、評価の結果を関数に渡すだけです。関数自体は、そのパラメーターの評価を処理しないか、それらのパラメーターがどのように評価されたかを必ずしも認識していません。
ただし、do
関数値を明示的に指定する場合、関数値を別の関数 (という名前の通常の関数) への呼び出しに渡すことを意味し、関数にまったく渡されなかったdo
追加のパラメーターの評価を魔法のように引き起こします。do
それは魔法ではありませんreturn/redo
。do
関数が機能する方法は、通常のショートカット戻り値で関数への参照を返し、ショートカット戻り値にフラグを付けて、返された関数を評価するために呼び出さ do
れたインタープリターに、あたかもそこで呼び出されたかのように伝えることです。コードで。これは基本的にトランポリンと呼ばれるものです。
ここで、Rebol のもう 1 つの興味深い機能に到達します。関数から戻り値をショートカットする機能はエバリュエーターに組み込まれていますが、実際にはreturn
関数を使用してそれを行うわけではありません。Rebol コードから見られるすべての関数は、内部のもの、さらにreturn
はdo
. 呼び出す関数は、これらのreturn
ショートカットの戻り値の 1 つを生成して返すだけです。残りは評価者が行います。
したがって、この場合、実際に起こったことは、最初から内部で行うことを行うコードがあったということですが、カールは、内部コードがそうする必要がないにもかかわらず、そのフラグを設定return/redo
するオプションを関数に追加することを決定しました。内部コードは内部関数を呼び出します。そして、彼は、オプションを外部から利用できるようにすること、またはその理由、またはそれが何をしたかについて、誰にも言いませんでした (すべてを言及することはできないと思います。誰が時間がありますか?)。Carl との会話と、私たちが修正してきたいくつかのバグに基づいて、R2 が機能を別の方法で処理したのではないかと疑っています。return
return
do
return/redo
これは、 の処理がreturn/redo
完全に関数評価に向けられていることを意味します。これが存在する理由のすべてだからです。それにオーバーヘッドを追加するdo
と、関数のオーバーヘッドが追加され、それを頻繁に使用します。利益がほとんどなく、まったく利益を得られないことを考えると、おそらくそれをブロックに拡張する価値はありません.
とはreturn/redo
いえ、機能としては、考えれば考えるほど便利になっているようです。最終日、私たちはこれが可能にするあらゆる種類のトリックを考え出しました。トランポリンは便利です。