当我预期nay
时,我对此代码打印yeah
感到有些惊讶:
(define five 5)
(case 5
[(five) "yeah"]
[else "nay"])
看at the racket documentation for case会更清楚:
所选子句是第一个带引号形式相等的基准的子句吗?到val-expr的结果。
所以关于报价。我很确定我还没有完全掌握lisps的报价可以买到什么。我从宏观和AST转换的角度理解它。但是我很困惑为什么在例如case
的情况下它有用??
我也很好奇,使用case
的这个规范,我可以用它来实现我想要的(比较实际值,而不是引用的值),或者我应该使用另一个构造那? (cond
虽然严格来说更强大,但对于简单的情况更为冗长,因为你必须在每个条件下重复谓词。)
答案 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
宏版本或自定义条件运算符来处理您的用例。