単純な違いは、シンボルは比較するのが非常に安いということです。eq?
2 つのシンボルを同一として比較するために使用できます。これは、コンパイル時に、コンパイラが基本的にすべてのシンボルを数値で列挙するためです。そのため、整数を比較しているだけで、非常に安価なマシン操作です。
これは、2 つの記号が同じ文字から構成されている場合にのみ同一であることを意味します。コード内で同じ文字を持つ 2 つのシンボルを識別する方法はありません。それらは定数であり、 と のように同じオブジェクト3
です3
。
ただし、2 つの文字列は、メモリの異なる場所に存在する異なるオブジェクトである可能性が非常に高く、それらを比較するには、すべての文字を個別に比較して一致させる必要があります。
したがって、シンボルは、たとえば次のことを考慮して、このために使用する必要があり、よく使用されます。
(assq 'symb '((foo 1 2 3) (bar symb) (symb "match")))
(symb "match")
これは、比較するのと同じくらい安価なリストを返します。
(assq 4 '((0 1 2 3) (1 symb) (4 "match")))
list を返します(4 "match")
。ただし、文字列をキーとして使用する場合は、比較にプロシージャassq
を使用するだけでは十分ではなく、構造を再帰的に比較するため、はるかにコストがかかるプロシージャを使用する必要があります。上記の実装は、確かにランダムアクセスではありませんが、インタープリターで連想配列と環境をモデル化する方法としてよく使用されるほど、実際には十分に安価です。eq?
assoc
equal?
編集:あなたが尋ねたように、ストリームで:
Scheme 標準はナイス ペアをサポートしています。1 つは と呼ばれる関数force
で、もう 1 つは と呼ばれる特別な形式delay
です。効果的に何
(delay (+ 1 2 3))
または、戻り値の代わりにあるその他のコード(+ 1 2 3)
は、いわゆる「プロミス」です。そのプロミスの答えの計算を遅らせますが、6
そこに到達したと評価されたときに結果が同じになることを約束します。これはまったく役に立たないように思えるかもしれませんが、結果が変更可能な変数に依存しているとします。
(define x 4) ; x is bound to 4.
(let ((promise (delay (+ 2 x)))) ; evaluating the expression now would result into 6, but we delay it.
(set! x 5) ; we change the value of x.
(display (force promise)) ; displays 7
(set! x 6) ; change it again
(force promise) ; ALSO displays 7, not 8
)
promise が実際に 1 回だけ評価されることが明らかになり、再度強制されると、最初に評価されたときと同じ値が生成されます。
これは通常、ストリーム、または概念的な「無限リスト」で使用されるものです。この場合のリストの cdr は、リストの残りの部分に対する promise であり、取得時に強制されます。それ以外の場合は、終了しない計算になります。たとえば、通常、次のように定義します。
(define (stream-cdr x) (force (cdr x))) ; it forces that promise to evaluate it.
; and optionally
(define stream-car car) ; same
これは引数を評価できないため、特別な形式である必要があります。
(define-syntax stream-cons
(syntax-rules ()
((stream-cons x y)
(cons x (delay y))))
これで、Scheme コンパイラまたはインタプリタは、任意の x および y に対してすべての(stream-cons x y)
toを変換します。(cons x (delay y))
簡単な例として、必要になるまで評価を遅らせたので、ゼロの無限リストを作成できます。
(define zero-stream (stream-cons 0 zero-streams))
ストリームを使用していない場合、リストは決して終了しないはずです。無駄ですが、アイデアを示しています。stream-cdr
空のリストに到達することなく、これを何度も何度も使用できます。同じ「0の無限リスト」が再び返されます。
より実用的な例は、すべてのフィボナッチ数のリストです。これはやや複雑です。
(define fibs
(stream-cons 0 (stream-cons 1
(stream-map + fibs (stream-cdr fibs))))
ストリーム マップは法線マップの類似物です。その定義は非常に複雑ですが、調べればわかると思います。それ自体がストリームを生成します。したがって、`(stream-map (lambda (xy) (+ 1 xy)) zeroes zeroes) は、完全に 1 で満たされたストリームを生成します。fibs ストリームは、それ自体で再帰的に定義されます。最初の 2 つの要素が与えられ、残りはたまたま fibs である 2 つのストリームと fibs 自体のストリーム cdr から計算されます。