在我的大学,我们不得不与Racket合作,因为我很喜欢它,我从No Starch购买了最近出版的书“Realm Of Racket”。
到目前为止,它很棒,但是,当他们试图解释 eq?的工作方式时,我无法弄清楚它们在第4章中的含义:
这个想法似乎得到了书中以下句子的证实:“eq?比较改变一个结构是否会改变其他结构......”太棒了!让我们将它与下面的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
让我们在Racket中试试这个:
(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,它本身指向'(2)。另外,他们是不是只是说一改变另一个就等于他们?
显然,现在我不明白为什么这不会像预期的那样表现,因此,我没有看到 eq?正在做什么。也许我错了,它与参考文献没有任何关系......
如果有人知道这一点,请分享你的智慧;)
答案 0 :(得分:7)
有关eq?
如何工作的技术说明,请查看当前的specification,您将找不到更详细的参考。或者只是检查关于主题的关于球拍的documentation,特别是程序eq?
,eqv?
和equal?
。关于你的问题 - 结果与Scheme代码中的预期和正确一样,让我们看看为什么。请注意,在Java中的这一行:
p1.x = 42;
您正在修改p1
和p2
所指向的相同对象。而在这一行:
(set! cons1 (cons 2 empty))
您正在创建新,不同的对象并设置cons1
以指向它,但cons2
仍然指向旧对象。您可以在上一行之后确认这一点,比较(eq? cons1 cons2)
将返回#f
。
重点是:这些例子并不等同。 Java示例处理由两个不同引用指向的单个对象,而Scheme示例处理两个对象和两个引用。
为了进行比较,这里是一个与Java代码类似的Scheme示例,并且按预期工作,因为在这里我们正在修改一个由两个引用指向的可变对象:
#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
答案 1 :(得分:3)
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?
- 在此结尾处:您会看到这两个不再是eq?
:他们是两个不同的价值观。
你真正想要的是在结构上做变异,而不是变量重新绑定。在Racket中,由于列表是不可变的,因此您需要使用允许变异的不同数据结构。 Vectors 是可以演示的一个示例数据类型。让我们再次“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]);
答案 2 :(得分:2)
你是对的,eq?
比较引用以确定相等性。这比equal?
更有效,eqv?
必须递归地比较对象。
Racket文档明确定义了每个以及相关的> (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
函数:
等于?
两个值相等?当且仅当它们是eqv?时,除非为特定数据类型另行指定。
具有相同规范的数据类型?包括字符串,字节串,对,可变对,向量,框,哈希表和可检查结构。在最后六个案例中,以递归方式定义了相等性;如果v1和v2都包含引用周期,则当值的无限展开相等时它们是相等的。
示例:
> (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
当量?
两个值是eqv?当且仅当它们是eq?时,除非为特定数据类型另行指定。
数字和字符数据类型是eqv的唯一数据类型?与eq?不同。
示例:
> (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
当量?
如果v1和v2引用相同的对象,则返回#t,否则返回#f。
示例:
{{1}}