これは、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。ややこしいですが、よく考えてみればわかります。
の実装に基づいて、car1 つの引数の関数を取り、それを数値 に適用するように見えます0。これは に適用dispatchされ0、 の定義からわかるようにdispatch、それが私たちが望んでいることです。そこの中はandとcond比較mしてor のどちらかを返します。この場合、 を返します。これは の最初の引数、つまりペアの最初のコンポーネントです! したがって、Scheme で通常のプリミティブが行うように、最初のコンポーネントが選択されます。01xyxconscar
に対してこれと同じロジックに従うとcdr、ほとんど同じように動作することがわかりますが、ペアの 2 番目のコンポーネントである , にcons2番目の引数を返します。y
これをよりよく理解するのに役立つことがいくつかあります。1 つは、第 1 章の評価の置換モデルの説明に戻ることです。これらの関数を使用する非常に単純な例について、その置換モデルを注意深く細心の注意を払って従うと、それらが機能することがわかります。
それほど面倒ではないもう 1 つの方法はdispatch、REPL で関数を直接試してみることです。以下では、変数は、によって返される関数pを参照するように定義されています。dispatchcons
> (define p (cons 1 2))
#<function> ;; what the REPL prints here will be implementation specific
> (p 0)
1
> (p 1)
2