5

我像这样在球拍中尝试过

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

有人对此有想法吗?

4

2 回答 2

11

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))))
于 2013-06-21T10:32:53.173 回答
11

Chris Jester-Young 的回答是正确的,但我还想强调另一点。标准and运算符是一个宏,它通过(基本上,如果不完全)(and a b c)变成(if a (if b c #f) #f). 这意味着 ifa为假,b并且c不被评估。

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

似乎仍然缺少一个选项:and-function-delaying-evaluation当且仅当a,bc全部返回 true 时返回 return 的一个选项,但它不评估,例如,b如果c产生afalse。实际上,这可以通过一个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您只需要通过将事物包装在函数中并让-like 函数调用这些函数来产生值来延迟评估。(在 Scheme 中,这可能是一个使用 Promise 的机会。)

于 2013-06-21T12:06:17.610 回答