【发布时间】:2013-06-18 09:47:12
【问题描述】:
我在 Racket 中这样尝试过
> (apply and '(1 2 3))
. and: bad syntax in: and
> (and 1 2 3)
3
有人对此有想法吗?
【问题讨论】:
-
因为
and是宏,而不是函数。 -
如果问题是重复的,但答案似乎添加了其他信息,那么有没有办法将两个问题混合在一起?
我在 Racket 中这样尝试过
> (apply and '(1 2 3))
. and: bad syntax in: and
> (and 1 2 3)
3
有人对此有想法吗?
【问题讨论】:
and 是宏,而不是函数。
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))))
【讨论】:
Chris Jester-Young 的answer 是对的,但我还想强调另一点。标准的and 运算符是一个宏,它通过(基本上,如果不完全)将(and a b c) 转换为(if a (if b c #f) #f),来延迟对其参数的评估。这意味着如果 a 为 false,则 b 和 c 不会被评估。
我们还可以选择定义and-function,以便(and-function a b c) 评估a、b 和c,并在值全部为真时返回真。这意味着所有a、b 和c 都会得到评估。 and-function 有一个很好的属性,你可以将它作为函数传递,因为它是一个函数。
似乎仍然缺少一个选项:and-function-delaying-evaluation 当且仅当a、b 和 c 都返回 true 时返回 return,但这不会评估,例如 @ 987654341@ 和 c 如果a 产生错误。实际上,这可以通过函数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 中,这可能是一个使用 Promise 的机会。)
【讨论】: