Scheme Flashcards
Let*
let*
会依次处理每个绑定,后续绑定可以引用之前的绑定变量。
普通的 let
会将所有绑定(变量和对应的值)并行处理,所有绑定的右侧表达式(值)在计算时不能依赖其他绑定。
(let ((x 2)
(y (+ x 1))) ; 错误,因为此时 x 未定义
(+ x y))
(let* ((x 2)
(y (+ x 1))) ; 正确,y = 2 + 1 = 3
(+ x y)) ; 返回 5
(define A
(lambda ()
(let* ((x 2)
(C (lambda (P)
(let ((x 4))
(P))))
(D (lambda ()
x))
(B (lambda ()
(let ((x 3))
(C D)))))
(B))))
写出上式的c版本,它打印什么?
def A():
x = 2
def C(P): x = 4 # 局部变量 x return P() def D(): return x # 捕获 A 的 x def B(): x = 3 # 局部变量 x return C(D) # 将 D 作为参数传递给 C return B()
调用 A
result = A()
print(result)
注意:scheme是严格的词法作用域
变量解析遵循 词法作用域(Lexical Scoping):
D 访问的是 let* 最初绑定的 x = 2,而不是 B 或 C 里的 x。
C 内部的 x = 4 只是局部变量,不影响 D。
Scheme 不支持动态作用域,所以 D 不会使用 C 里的 x = 4,也不会用 B 里的 x = 3。
最终 A 返回 2。
define定义函数的语法
(define (函数名 参数1 参数2 …) 代码体)
lambda定义函数的语法
(lambda (参数1 参数2 …) 函数体)
(let ([a 1] [b 2]) a)转换成lambda式子
可以拆解为:
a 绑定到 1
b 绑定到 2
计算 a 的值,返回 1
((lambda (a b) a) 1 2)
使用 letrec 递归计算阶乘5
(letrec ([fact (lambda (n)
(if (< n 2) 1 (* n (fact (- n 1)))))])
(fact 5))
尾递归是什么?
尾递归是 递归调用后,不再进行额外计算,直接返回结果,这样编译器可以优化,不占用新的栈帧,使得递归执行效率与 循环 类似。
(define (factorial-tail n acc)
(if (= n 0)
acc
(factorial-tail (- n 1) (* n acc)))) ;; 递归调用时,结果已经计算完毕
;;调用
(factorial-tail 5 1)
前面加的意思
比如'(1 2 3)
前面加,的意思
比如
(define x 10)
(1 2 ,x)
,@的意思
比如(define lst ‘(3 4))(1 2 ,@lst)
如果是
(1 2 ,lst) 结果是什么?
‘(1 2 3) ; 结果是 (1 2 3),所有元素都是字面值。
在 Scheme 里,前面加上 “`”(反引号,Backquote,或 Quasiquote)是一种 延迟求值 机制,主要用于构造列表,同时允许部分元素求值。
(define x 10)
`(1 2 ,x) ; 结果是 (1 2 10),x 被求值
在 quasiquote 中,使用 ,(逗号,unquote) 可以让某个部分被求值。
(define lst ‘(3 4))(1 2 ,@lst) ; 结果是 (1 2 3 4)
,@(unquote-splicing) 用于把列表的内容拆开,而不是作为一个整体放入。
(1 2 ,lst) ; 结果是 (1 2 (3 4))
cons
在 Lisp 里,cons
作用于两个元素,构造一个有序对(pair),通常用于构造链表:
(cons 1 (cons 2 (cons 3 nil))) ;; 生成 (1 2 3)
等价于`(1 2 3)