3

Scheme 中的 'and' 将忽略错误 '除以 0',例如 (and (negative? (random 100)) (/1 0)) 返回 #f。它是如何做到的?

i (define (∧ b₀ b₁) (if (not b₀) #f b₁)), (∧ (negative? (random 100)) (/ 1 0)) 仍然进入“除以 0”错误。

4

3 回答 3

5

您不能and直接定义为函数,因为 Scheme 是严格的——这意味着函数参数总是在传递给函数之前被评估。

and但是,您可以使用宏定义适当的短路。这是 Racket 中最简单的版本:

(define-syntax-rule (my-and a b) (if a b #f))

syntax-rules或标准方案中使用的等效形式:

(define-syntax my-and
  (syntax-rules ()
    [(_ a b) (if a b #f)]))

宏不是普通功能。相反,它是在编译时运行的句法转换。当您and在代码中使用 new 时,它会“扩展”为相应的if表达式。所以:

(my-and #f (/ 1 0))

变成

(if #f (/ 1 0) #f)

在你的程序运行之前。由于if内置于语言中,因此具有正确的行为。

由于宏不是函数,这也意味着您不能作为参数传递。所以你不能and直接使用写一个折叠——你必须把它包装成一个 lambda。

为了更忠实于原始and,您可以my-and通过使宏递归来定义采用任意数量的参数。要在宏中定义“剩余参数”,请使用特殊...关键字:

(define-syntax my-and
  (syntax-rules ()
    [(_)         #t]
    [(_ a)       a]
    [(_ a b ...) (if a (my-and b ...) #f)]))

如果您使用的是诸如 Racket#lang lazy或 Haskell 之类的惰性语言,则无需在此处使用宏。你可以直接定义and

#lang lazy
(define (and a b) (if a b #f))

或在 Haskell 中:

and a b = if a then b else False

作为正常功能,它将具有正确的行为。您可以将其传递and给折叠,它甚至会在遇到False!时立即停止评估列表 看一看:

Prelude> foldl and True [True, False, error "fail"]
False

error在 Haskell 中的错误就像1/0. 由于 Haskell 是静态类型的,因此参数and必须是布尔值,因此您不能1/0直接使用。)

于 2013-11-04T05:29:01.643 回答
1

像大多数语言一样,Scheme 的逻辑AND使用短路求值,这意味着它的右操作数只有在左操作数为真时才会被求值。如果左操作数为假,则无论右操作数的值如何,表达式的结果都必须为假,因此如果左操作数的计算结果为假,则立即返回假,根本不计算右操作数。

准确地说,这是规范中的语言(我是 R5RS 的第 4.2.1 节,但这不是一个可能在规范修订之间改变的领域):

(and <test1> ... )

<test>表达式从左到右计算,返回第一个计算结果为假值的表达式的值(参见第 6.3.1 节)。不会评估任何剩余的表达式。

于 2013-11-04T05:12:18.027 回答
0

布尔快捷方式。“and”的第一个参数计算为假,因此结果必须为假,并且没有理由计算第二个参数(因此会导致错误)。

于 2013-11-04T05:11:35.557 回答