これは、Scheme でファーストクラスの関数を利用する奇妙な (そしておそらくより素晴らしい) 例の 1 つです。私が最初に見たリトル・シェイマーにも似たようなものがあり、何日も頭をかきむしっていたのを覚えています。分かりやすく説明していただけると助かりますが、わかりにくかったらすみません。
プリミティブcons
、car
、およびcdr
は既に Scheme で実装されているため、理解していると思いますが、念のために言っておくとcons
、 はペアを構築し、ペアcar
の最初のコンポーネントを選択して返しcdr
、2 番目のコンポーネントを選択して返します。これらの関数を使用した簡単な例を次に示します。
> (cons 1 2)
(1 . 2)
> (car (cons 1 2))
1
> (cdr (cons 1 2))
2
貼り付けたcons
、car
、およびのバージョンは、まったく同じように動作するはずです。cdr
その方法をお見せします。
まず、car
とcdr
は の範囲内で定義されていませんcons
。コード スニペットでは、3 つすべて ( cons
、car
、およびcdr
) がトップレベルで定義されています。関数dispatch
は、内部で定義されている唯一のものですcons
。
この関数cons
は 2 つの引数を取り、1 つの引数の関数を返します。これについて重要なことは、これらの 2 つの引数がdispatch
返されている内部の function に見えるということです。それについてはすぐに説明します。
リマインダーで言ったようにcons
、ペアを構築します。このバージョンのcons
は同じことを行うはずですが、代わりに関数を返しています! 1 番目と 2 番目のコンポーネントを取得できる限り、ペアがどのようにメモリに実装または配置されるかはあまり気にしません。
したがって、この新しい関数ベースのペアではcar
、ペアを呼び出して引数として渡し、最初のコンポーネントを取得できる必要があります。の定義ではcar
、この引数は と呼ばれz
ます。cons
これらの新しい、car
、および関数を使用して上記と同じ REPL セッションを実行する場合cdr
、 の引数z
はcar
関数ベースのペアにバインドされcons
ますdispatch
。ややこしいですが、よく考えてみればわかります。
の実装に基づいて、car
1 つの引数の関数を取り、それを数値 に適用するように見えます0
。これは に適用dispatch
され0
、 の定義からわかるようにdispatch
、それが私たちが望んでいることです。そこの中はandとcond
比較m
してor のどちらかを返します。この場合、 を返します。これは の最初の引数、つまりペアの最初のコンポーネントです! したがって、Scheme で通常のプリミティブが行うように、最初のコンポーネントが選択されます。0
1
x
y
x
cons
car
に対してこれと同じロジックに従うとcdr
、ほとんど同じように動作することがわかりますが、ペアの 2 番目のコンポーネントである , にcons
2番目の引数を返します。y
これをよりよく理解するのに役立つことがいくつかあります。1 つは、第 1 章の評価の置換モデルの説明に戻ることです。これらの関数を使用する非常に単純な例について、その置換モデルを注意深く細心の注意を払って従うと、それらが機能することがわかります。
それほど面倒ではないもう 1 つの方法はdispatch
、REPL で関数を直接試してみることです。以下では、変数は、によって返される関数p
を参照するように定義されています。dispatch
cons
> (define p (cons 1 2))
#<function> ;; what the REPL prints here will be implementation specific
> (p 0)
1
> (p 1)
2