8

私の大学では、Racket を使用する必要がありました。私はそれが好きだったので、最近出版された本「Realm Of Racket」を No Starch から購入しました。

ここまでは素晴らしいのですが、第 4 章でどのようにeq? 作品:

  1. 最初に、彼らはどのように等しいかを説明しますか?2 つの値が同一の部分で構成されているかどうかを比較します。OK、問題ありません。わかりました:等しいですか? Java のequals(someObject)メソッドとほとんど同じことを行います。2 つのオブジェクト/構造体/内容が同じである場合、#t が返されます。
  2. それから、私は考えましたJava の==演算子と同等である必要があります。これは内容ごとに比較するのではなく、参照に基づいて比較します。
  3. この考えは、本の中の次の文によって確認されたようです: "eq? は、1 つの構造を変更すると、他の構造が変更されるかどうかを比較します..."素晴らしい! 次の Java コードと比較してみましょう。

    Point p1 = new Point(5, 5);
    Point p2 = p1;
    System.out.println(p1 == p2);   // true, since the reference has been copied.
    System.out.println(p1.x);       // 5
    System.out.println(p2.x);       // 5
    p1.x = 42;
    System.out.println(p1.x);       // 42
    System.out.println(p2.x);       // Accordingly, 42
    

    ラケットでこれを試してみましょう:

    (define cons1 (cons 1 empty))
    (define cons2 cons1)
    (eq? cons1 cons2)           ;; #t, since the refernce has been copied.
    (set! cons1 (cons 2 empty))
    cons1                       ;; Returns '(2) - as expected.
    cons2                       ;; Still returns '(1).
    

    なんで?cons2 は cons1 を指し、cons1 自体は '(2) を指します。また、一方が他方を変更するとすぐに同等であると言っただけではありませんか。

明らかに、今のところ、これが期待どおりに動作しない理由がわかりません。そのため、どの式が表示されませんか? やっています。多分私は間違っていて、参照とは何の関係もありません...

誰かがこれについて知っているなら、あなたの知恵を共有してください;)

4

3 に答える 3

8

がどのように機能するかの技術的な説明についてはeq?、現在の仕様を参照してください。詳細なリファレンスは見つかりません。または、この件に関する Racket のドキュメント、特に手順eq?eqv?を確認してくださいequal?。あなたの質問に関して - 結果は予想どおりで、Scheme コードで正しいです。その理由を見てみましょう。Java の次の行に注意してください。

p1.x = 42;

との両方が指している同じオブジェクトを変更しています。一方、この行では:p1p2

(set! cons1 (cons 2 empty))

新しい、別のオブジェクトとcons1それを指す設定を作成していますcons2が、まだ古いオブジェクトを指しています。これを確認できます。前の行の後、比較(eq? cons1 cons2)は を返し#fます。

ポイントは、例は同等ではないということです。Java の例は、2 つの異なる参照によって指されている 1 つのオブジェクトを扱いますが、Scheme の例は、2 つのオブジェクトと 2 つの参照を扱います。

比較のために、Java コードに似たスキームの例を次に示します。ここでは、2 つの参照によってポイントされている単一の変更可能なオブジェクトを変更しているため、期待どおりに動作します。

#lang racket
(require scheme/mpair) ;; `m` stands for "mutable"

(define p1 (mlist 5 5))
(define p2 p1)

(eq? p1 p2)       ;; #t
(mcar p1)         ;;  5
(mcar p2)         ;;  5

(set-mcar! p1 42)
(eq? p1 p2)       ;; #t
(mcar p1)         ;; 42
(mcar p2)         ;; 42
于 2013-07-10T13:50:57.863 に答える
4

ああ: 構造を変更していることを確認する必要があります。あなたの例では、実際には既存の値の構造を変更するのではなく、まったく新しい値を構築しcons1てそれに向けています。あなたは正しい概念を持っています: eq?Java の==.

類推するために、ここに Java フォームの間違いを示します。何が問題だったかがわかります。

int[] lst1 = new int[] { 1 };       // (define cons1 (cons 1 empty))
int[] lst2 = lst1;                  // (define cons2 cons1)
System.out.println(lst1 == lst2);   // (eq? cons1 cons2)
lst1 = new int[] { 2 };             // (set! cons1 (cons 2 empty))
System.out.println(lst1[0]);        // (list-ref cons1 0)
System.out.println(lst2[0]);        // (list-ref cons2 0)

実際、この時点eq?で、最後に -ness を確認する必要があります。この 2 つはもはや ではなく、2eq?つの別個の値であることがわかります。

本当に必要なのは、変数の再バインドではなく、構造体で突然変異を行うことです。Racket では、リストは不変であるため、変更が可能な別のデータ構造を使用する必要があります。 ベクトルは、実証できるデータ型の 1 つの例です。これをもう一度 "Rosetta" してみましょう。アナロジーがわかります。

(define vec1 (vector 1))          ;; int[] vec1 = new int[] { 1 };
(define vec2 vec1)                ;; int[] vec2 = vec1;
(eq? vec1 vec2)                   ;; System.out.println(vec1 == vec2);
(vector-set! vec1 0 2)            ;; vec1[0] = 2;
(vector-ref vec1 0)               ;; System.out.println(vec1[0]);
(vector-ref vec2 0)               ;; System.out.println(vec2[0]);
于 2013-07-10T16:14:53.587 に答える
3

eq?参照を比較して同等性を判断するのは正しいです。equal?これは、オブジェクトを再帰的に比較する必要があるよりもはるかに効率的です。

Racketのドキュメントでは、それぞれと関連するeqv?関数が明示的に定義されています。

同等?

2 つの値は等しいか? 特定のデータ型について特に指定されていない限り、それらが eqv? である場合に限ります。

equal? をさらに指定したデータ型 文字列、バイト文字列、ペア、変更可能なペア、ベクトル、ボックス、ハッシュ テーブル、および検査可能な構造が含まれます。最後の 6 つのケースでは、等式が再帰的に定義されています。v1 と v2 の両方に参照サイクルが含まれている場合、値の無限展開が等しい場合、それらは等しくなります。

例:

> (equal? 'yes 'yes)
#t
> (equal? 'yes 'no)
#f
> (equal? (expt 2 100) (expt 2 100))
#t
> (equal? 2 2.0)
#f
> (equal? (make-string 3 #\z) (make-string 3 #\z))
#t

同等?

2 つの値は eqv? 特定のデータ型について特に指定されていない限り、それらが eq? である場合に限ります。

数字と文字のデータ型だけが、どの eqv? eq? とは異なります。

例:

> (eqv? 'yes 'yes)
#t
> (eqv? 'yes 'no)
#f
> (eqv? (expt 2 100) (expt 2 100))
#t
> (eqv? 2 2.0)
#f
> (eqv? (integer->char 955) (integer->char 955))
#t
> (eqv? (make-string 3 #\z) (make-string 3 #\z))
#f

等?

v1 と v2 が同じオブジェクトを参照する場合は #t を返し、そうでない場合は #f を返します。

例:

> (eq? 'yes 'yes)
#t
> (eq? 'yes 'no)
#f
> (let ([v (mcons 1 2)]) (eq? v v))
#t
> (eq? (mcons 1 2) (mcons 1 2))
#f
> (eq? (make-string 3 #\z) (make-string 3 #\z))
#f
于 2013-07-10T13:55:02.737 に答える