let
的语义
结论: let
scope 会立即求值 (包括绑定和绑定之后的表达式, 也就是 let
之外的一层括号所包裹的范围), 即使你把 let
嵌套在内层函数里而且这个函数还完全没有被调用.
示例:
(define (comp x)
(if (> 3 x)
(display "then-clause")
(display "else-clause")
)
#|
(define foo1
(let ((bar1 (/ 2 0))) ; * evaluted immediately
(display "should not be printed")
)
)
|#
(define foo2
(let ((bar2 (/ 5 2))) ; * evaluted immediately
(newline)
(display "let in `foo2`, bar2: ")
(display bar2)
)
)
)
运行示例:
-
foo1
: 可以看到let
绑定被求值了, 否则不会抛出除零异常.prompt> (comp 4) else-clause ;Division by zero signalled by /. ;To continue, call RESTART with an option number: ;snip
-
foo2
: 可以看到let
绑定后的表达式被求值了, 否则不会有display
的输出prompt> (comp 2) then-clause let in `foo2`, bar2: 5/2 ;Unspecified return value
if
的语义
if
的语义: 根据对条件式进行求值,根据求值结果决定继续对 then/else 分支进行求值.
可以与此对照参考的材料是 SICP (2nd Edition) Exercise 1.6,此题中使用 abstraction ,通过 cond
定义了 new-if
:
(define (new-if predicate then-clause else-clause)
(cond (predicate then-clause)
(else else-clause)
)
)
函数应用的语义是(applicative mode evaluation):先对参数(比如这里的then-clause
和 else-clause
)进行求值,然后进行函数应用,这也是为什么不能在then-clause
或 else-clause
里写递归表达式,因为它们会先被求值,而不是根据 predicate 的值决定是否求值,于是递归就无穷无尽了. if
/cond
/… 是 special form, 相较于通过 define
定义的 abstraction, 语义是特殊的. 我喜欢 new-if
这个例子,它简洁而有力地体现了 lisp 元编程的特性,通过此例也可以更好地接纳 lisp 里大量的括号,根据 substitution model, 这里的 predicate
, then-clause
, else-clause
可以替换成你需要的表达式, 而表达式就是被括号包裹的, 即 括号是表达式的边界, 你可以把被括号包裹的表达式放在任意的参数位 (当然要符合 abstraction 隐式的对于参数的类型约束).
let
立即求值引发的错误实例
对于这样素数判断的一段代码:
(define (prime? x)
(if (or (= x 1) (= x 2))
#t
test_prime
)
(define (divisible? y)
(= 0 (remainder x y))
)
(define (iter_biggest_divisor y)
(cond ((= y 1) 1)
((divisible? y) y)
(else (iter_biggest_divisor (- y 1)))
)
)
(define test_prime
(let ((biggest_divisor (iter_biggest_divisor (quotient x 2)) )) ; Notice
(display biggest_divisor)
(if (= biggest_divisor 1)
#t
#f
)
)
)
)
运行的结果是:
prompt> (prime? 1)
;The object 0, passed as the second argument to integer-remainder, is not in the correct range.
prompt> (prime? 2)
1
;Value: #t
按照预期, (prime? 1)
和 (prime? 2)
都应该直接返回 #t
, 而不是前者报错, 后者呈现出 (display biggest_divisor)
的行为. 根据前文的陈述, 原因是: 整个 let
scope 都被求值了.