为什么(申请和'(1 2 3))不起作用,而(和1 2 3)在R5RS中有效?

时间:2013-06-21 09:49:06

标签: lisp scheme

我在这样的Racket中尝试过它

> (apply and '(1 2 3))
. and: bad syntax in: and
> (and 1 2 3)
3

有没有人有这方面的想法?

2 个答案:

答案 0 :(得分:10)

Chris Jester-Young的answer是对的,但还有一点我想强调一点。标准and运算符是一个宏,通过(基本上,如果不完全正确)将(and a b c)转换为(if a (if b c #f) #f)来延迟对其参数的评估。这意味着如果a为false,则bc不会被评估。

我们还可以选择定义and-function(and-function a b c)评估abc,并在值为0时返回true都是真的。这意味着评估了所有abcand-function有一个很好的属性,你可以将它作为函数传递,因为它是一个函数。

还有一个选项似乎缺失:只有当and-function-delaying-evaluationab都返回true时,c才会返回返回如果b产生错误,则不评估ca。实际上,这可以通过函数and-funcalling-function来实现,该函数要求其参数是函数列表。例如:

(define (and-funcalling-function functions)
  (or (null? functions)
      (and ((car functions))
           (and-funcalling-function (cdr functions)))))

(and-funcalling-function 
 (list (lambda () (even? 2))
       (lambda () (odd? 3))))
; => #t

(and-funcalling-function 
 (list (lambda () (odd? 2))
       (lambda () (even? 3)))) ; (even? 3) does not get evaluated
; => #f

使用宏和这个习惯用法,我们实际上可以用标准的and语义实现一些东西:

(define-syntax standard-and
  (syntax-rules ()
    ((standard-and form ...)
     (and-funcalling-function (list (lambda () form) ...)))))

(macroexpand '(standard-and (odd? 2) (even? 3)))
; =>
; (and-funcalling-function 
;  (list (lambda () (odd? 2))
;        (lambda () (even? 3))))

当然,要从中汲取的教训是,你可以拥有and类似的功能,你可以传递并仍然得到延迟评估;你只需要通过在函数中包装东西来延迟评估,并让and - 像函数调用这些函数来生成值。 (在Scheme中,这可能是使用promises的机会。)

答案 1 :(得分:8)

and不是函数,它是一个宏,所以你不能像函数那样传递它。

and是宏的原因是启用短路行为。您可以制作自己的非短路版本:

(define (my-and . items)
  (if (null? items) #t
      (let loop ((test (car items))
                 (rest (cdr items)))
        (cond ((null? rest) test)
              (test (loop (car rest) (cdr rest)))
              (else #f)))))

my-and 可以apply一起使用。

为了比较,这是宏(它做短路)的样子:

(define-syntax and
  (syntax-rules ()
    ((and) #t)
    ((and test) test)
    ((and test rest ...) (if test
                             (and rest ...)
                             #f))))