计划和案例中的案例和报价球拍

时间:2016-07-10 08:31:42

标签: scheme lisp racket

当我预期nay时,我对此代码打印yeah感到有些惊讶:

(define five 5)
(case 5
  [(five) "yeah"]
  [else "nay"])

at the racket documentation for case会更清楚:

  

所选子句是第一个带引号形式相等的基准的子句吗?到val-expr的结果。

所以关于报价。我很确定我还没有完全掌握lisps的报价可以买到什么。我从宏观和AST转换的角度理解它。但是我很困惑为什么在例如case的情况下它有用??

我也很好奇,使用case的这个规范,我可以用它来实现我想要的(比较实际值,而不是引用的值),或者我应该使用另一个构造那? (cond虽然严格来说更强大,但对于简单的情况更为冗长,因为你必须在每个条件下重复谓词。)

3 个答案:

答案 0 :(得分:3)

Racket文档给出了这个语法:

(case val-expr case-clause ...)

,其中

case-clause     =       [(datum ...) then-body ...+]
                |       [else        then-body ...+]

让我们与您的示例进行比较:

(define five 5)
(case 5             ; (case val-expr
  [(five) "yeah"]   ;    [(datum) then-body1]
  [else "nay"])     ;    [else    then-body2])

我们发现(five)被解释为(datum)。这意味着five是 一段数据(这里是符号),而不是表达式(稍后要评估)。

您的案例示例评估如下:

首先评估表达式5。结果是值5。 现在我们一次看一个条款。第一个条款是[(five) "yeah"]。 值5是否与equal?中的某个基准相等(在(five)意义上)?不,所以我们看下一个条款:[else "nay"]。它是else - 子句,因此评估表达式"nay",结果为值"nay"case - 表达式的结果因此是值"nay"

注1:case - 子句的左侧是基准(想一想:它们是隐式引用的)。

注2:val-expr的结果与使用equal?的子句基准进行比较。 (这与Scheme相反,后者使用eqv?

更新

为什么要包含case?让我们看看如何使用cond编写示例:

(define five 5)
(let ([val five])
    (cond 
      [(member val '(five))      "yeah"]
      [(member val '(six seven)) "yeah"]  ; added 
      [else                      "nay"])

这表明没有case就可以使用cond。 但是 - 哪个版本更容易阅读?

对于case表达式,很容易看出该值与哪些基准进行比较。 在这里必须仔细查看基准面。同样在我们事先知道的例子中,我们试图在几个基准列表中找到值。一般来说,我们需要更仔细地检查cond - 表达式,以了解发生了什么。

简而言之:拥有case - 表达式可以提高代码的可读性。

对于历史感兴趣的人:https://groups.csail.mit.edu/mac/ftpdir/scheme-mail/HTML/rrrs-1986/msg00080.html讨论是否对eqv?使用equal?case

更新2

我试图回答:

  

我仍然不清楚报价与仅仅在价值上工作。   我特别想知道为什么要做报价,为什么要做基准工作呢   致力于价值观。还没有得到那个。

这两种方法都有意义。

为了论证,让我们看一下case使用表达式而不是子句左侧的基准面的情况。同样遵循Scheme传统,我们假设eqv?用于比较。我们打电话给这样一个 ecase的case-expression(表达式案例的缩写)。

语法变为:

(ecase val-expr ecase-clause ...)

,其中

ecase-clause     =      [(expr ...)  then-body ...+]
                |       [else        then-body ...+]

您的示例现在变为:

(define five 5)
(ecase five
  [('five) "yeah"]
  [else    "nay")

这看起来并不太糟糕,结果就是我们习惯的结果。 但请考虑这个例子:

(ecase '(3 4)
  [('five (list 3 4) "yeah"]
  [else              "nay")

结果将是" nay"。评估表达式'(3 4)(list 3 4)得出的两个列表在eqv?意义上不相等。 这表明,如果选择使用eqv?进行比较,则在左侧显示表达式将无济于事。唯一使用eqv?原子值的值 - 因此也可以使用隐式引用并将左侧限制为基准。

现在如果使用equal?,那么在左侧使用表达式会更有意义。 case的原始Racket版本与Scheme中的版本(即使用eq?)相同,后来更改为使用equal?。如果case是从头设计的,我认为,表达式将被允许而不是基准。

唯一剩下的问题:为什么Scheme的作者选择eqv?而不是equal?进行比较?我的直觉是,原因是表现(当天比现在更重要)。链接到rrrs-authors邮件列表的帖子有两个选项。如果你再挖一点,你可能会找到答案。

答案 1 :(得分:3)

问题是case引入了隐式 quote表单,这会导致您的示例适用于'five(其值为'five)而不是five(其值为5)。

我几乎从不使用case因为这个问题。相反,我使用带有match模式的球拍==形式:

(define five 5)
(define (f x)
  (match x
    [(== five) "yeah"]
    [_ "nay"]))
(f 5) ; "yeah"
(f 6) ; "nay"

仅在值"yeah"上生成5,就像您预期的那样。如果您希望它在等于"yeah"five时返回six,则可以使用or模式:

(define five 5)
(define six 6)
(define (f x)
  (match x
    [(or (== five) (== six)) "yeah"]
    [_ "nay"]))
(f 5) ; "yeah"
(f 6) ; "yeah"
(f 7) ; "nay"

如果您真的想要与quote d基准匹配,可以通过编写显式 quote表单来实现。

(define (f x)
  (match x
    [(or 'five 'six) "yeah"]
    [_ "nay"]))
(f 5) ; "nay"
(f 6) ; "nay"
(f 7) ; "nay"
(f 'five) ; "yeah"
(f 'six) ; "yeah"

当您使用quote时,这些case表单是隐式和不可见的,潜伏在那里等待引起混淆。

答案 2 :(得分:1)

我现在无法找到引用,但case语句在其不同的子句中使用文字的,未评估的数据,因为它既是常见的用例,又更容易受到高效编译的影响。 您可以编写自己的Clojure condp宏版本或自定义条件运算符来处理您的用例。