ここ(例の下)のサンプルコードを理解しようとしています。私はそのパラメータ化構造を理解していません。そのためのドキュメントはここにありますが、役に立ちません。それは何をするためのものか?
3 に答える
parameterize
「動的スコープ」の値を持つために使用されます。でパラメータを取得しますmake-parameter
。パラメータ自体は関数として動作します。入力なしで呼び出すとその値を取得し、1つの値で呼び出すと値が設定されます。例えば:
> (define p (make-parameter "blah"))
> (p)
"blah"
> (p "meh")
> (p)
"meh"
多くの関数(多くのプリミティブ関数を含む)は、動作をカスタマイズする方法としてパラメーターを使用します。たとえばprintf
、パラメータの値であるポートを使用して印刷しcurrent-output-port
ます。ここで、何かを出力する関数があるとします。
> (define (foo x) (printf "the value of x is ~s\n"))
通常、この関数を呼び出すと、画面に何かが印刷されますが、ファイルなどに何かを印刷するために使用したい場合もあります。あなたはこれを行うことができます:
(define (bar)
(let ([old-stdout (current-output-port)])
(current-output-port my-own-port)
(foo some-value)
(current-output-port old-stdout)))
これに関する問題の1つは、実行するのが面倒なことですが、マクロを使用すると簡単に解決できます。(実際、PLTには、一部の言語でそれを行う構造がまだあります。)しかし、ここにはさらに問題があります。呼び出しによってランタイムエラーが発生したfluid-let
場合はどうなりますか?foo
これにより、システムが不良状態になり、すべての出力がポートに送られる可能性があります(また、何も出力されないため、問題は発生しません)。そのための解決策(これもfluid-let
使用します)は、パラメーターの保存/復元をで保護するdynamic-wind
ことです。これにより、エラーが発生した場合(さらに、継続について知っている場合)、値が引き続き復元されます。
だから問題は、グローバル変数を使用するのではなく、パラメータを持つことのポイントは何fluid-let
ですか?グローバルだけでは解決できない問題がさらに2つあります。1つは、複数のスレッドがある場合に発生することです。この場合、値を一時的に設定すると、他のスレッドに影響し、標準出力に出力したい場合があります。パラメータは、スレッドごとに特定の値を設定することでこれを解決します。何が起こるかというと、各スレッドはそれを作成したスレッドから値を「継承」し、1つのスレッドでの変更はそのスレッドでのみ表示されます。
もう1つの問題はもっと微妙です。数値のパラメーターがあり、次のことを実行するとします。
(define (foo)
(parameterize ([p ...whatever...])
(foo)))
Schemeでは、「末尾呼び出し」が重要です。これらは、ループなどを作成するための基本的なツールです。 parameterize
パラメータ値を一時的に変更しながら、これらの末尾呼び出しを保持できるようにする魔法を実行します。たとえば、上記の場合、スタックオーバーフローエラーが発生するのではなく、無限ループが発生します。これらの各式は、クリーンアップを実行する必要がなくなっparameterize
た以前の式が存在する場合に、何らかの方法で検出できるようになります。parameterize
最後に、parameterize
実際にはPLTの2つの重要な部分を使用してその仕事を行います。スレッドセルを使用してスレッドごとの値を実装し、継続マークを使用して末尾呼び出しを保持できるようにします。これらの各機能は、それ自体が便利です。
parameterize
ブロックの外部の値に影響を与えることなく、ブロックの期間中、特定のパラメーターを指定された値に設定します。
Parameterizeは、ラムダを使用せずに、既存の関数内で値を動的に再バインドできる手段です。実際には、引数を渡してラムダを使用してバインドするよりも、パラメーター化を使用して関数内の値を再バインドする方がはるかに簡単な場合があります。
たとえば、使用するライブラリがHTMLをstdoutに出力するが、便宜上、その値を文字列にキャプチャして、さらに操作を実行するとします。ライブラリ設計者には、これを簡単にするために少なくとも2つの選択肢があります。1)関数の引数として出力ポートを受け入れるか、2)current-output-port値をパラメーター化します。1は醜くて面倒です。最も可能性の高い動作はstdoutに出力することであるため、2の方が優れていますが、文字列ポートに出力する場合は、その関数の呼び出しをパラメーター化するだけです。