怎么样?在Racket工作?

时间:2013-07-10 13:40:47

标签: lisp scheme racket

在我的大学,我们不得不与Racket合作,因为我很喜欢它,我从No Starch购买了最近出版的书“Realm Of Racket”。

到目前为止,它很棒,但是,当他们试图解释 eq?的工作方式时,我无法弄清楚它们在第4章中的含义:

  1. 首先,他们解释是否相等?比较两个值是否由相同的部分组成。好的,没问题,我明白了:相等?与Java的 equals(someObject)方法完全相同。如果两个对象/结构/内容相同,则返回#t。
  2. 然后,我想, eq?必须等同于Java的 == 运算符,它不会按内容进行比较,而是基于引用。
  3. 这个想法似乎得到了书中以下句子的证实:“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)。另外,他们是不是只是说一改变另一个就等于他们?

  4. 显然,现在我不明白为什么这不会像预期的那样表现,因此,我没有看到 eq?正在做什么。也许我错了,它与参考文献没有任何关系......

    如果有人知道这一点,请分享你的智慧;)

3 个答案:

答案 0 :(得分:7)

有关eq?如何工作的技术说明,请查看当前的specification,您将找不到更详细的参考。或者只是检查关于主题的关于球拍的documentation,特别是程序eq?eqv?equal?。关于你的问题 - 结果与Scheme代码中的预期和正确一样,让我们​​看看为什么。请注意,在Java中的这一行:

p1.x = 42;

您正在修改p1p2所指向的相同对象。而在这一行:

(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}}