3. Let 表达式¶
3.1. Let 块¶
在 使用辅助函数编写 reverse 和 split 函数 中,我们引入了辅助函数,并考察了下面这个 split 函数。
var split = function (pivot,list) {
if (fp.isNull(list)) {
return [[],[]];
} else {
if (fp.isLT(fp.hd(list), pivot)) {
return fp.makeList(
fp.cons(fp.hd(list), fp.hd(split(pivot,fp.tl(list)))), // Call split
fp.hd(fp.tl(split(pivot,fp.tl(list)))) // Call again
);
} else {
return fp.makeList(
fp.hd(split(pivot,fp.tl(list))), // Call split
fp.cons(fp.hd(list), fp.hd(fp.tl(split(pivot,fp.tl(list))))) // Call again
);
}
}
};
虽然这样能正确工作,但它本质上效率低下,因为它总是对 split 进行两次递归调用,而这两次调用必然产生完全相同的结果。
更好的做法是缓存对 split 的一次调用结果,然后用这个缓存结果 代替那两次调用。如果我们能够声明一个局部于需要它的作用域的符号, 并把调用 split 的结果“赋”给该符号,就能在 SLang 1 中做到 这一点。这样一来,我们不必调用 split 两次,只需两次使用通过 与符号关联而缓存下来的结果。正是出于这一考虑,我们在 SLang 1 中 引入 let 块 。请看下面三个 let 块 的用法示例。在每个示例 中, let 之后对符号的赋值会在由关键字 in 和 end 括起来 的表达式中使用。
示例 1
let
x = 1
y = 2
in
+(x,y)
end
示例 2
let
x = 1
in
let
f = fn(y) => +(y,x)
in
let
x = 2
in
(f 3)
end
end
end
示例 3
let
x = 1
sqr = fn (x) => *(x,x)
in
let
f = fn(y) => +(y,x)
in
let
x = 2
in
+(x,(sqr (f x)))
end
end
end
有趣的是, let 块 (或称 let 表达式 )不过是 SLang 1 中某个 已有构造的 语法糖 。也就是说,当 SLang 1 解释器遇到 let 块 时,它可以在构建抽象语法树的过程中直接把它“翻译”成那个已有构造。 要想弄清这个已有结构是什么,请确定下面每句话中各个问号处应填 什么。
语句 1:当我们求值 let x = 1 y = 2 in <exp> end 时,我们返回 ??? 的值,其环境把 ??? 和 ??? 分别绑定到 ??? 和 ??? 。
语句 2:当我们求值 (fn (x,y) => <exp> 1 2) 时,我们返回 ??? 的值,其环境把 ??? 和 ??? 分别绑定到 ??? 和 ??? 。
通过做下面的练习题来检验你填写问号是否正确。
3.2. 作为语法糖的 Let 块¶
下面的随机化问题着重考察作为 语法糖 的 let 表达式 。连续三次 正确解答即可获得学分。
3.3. 嵌套 Let¶
下面的随机化问题着重考察嵌套 let 表达式 的求值。连续三次正确 解答即可获得学分。
提交答案时,请记住给出完整的指称值,例如 [ "Num", 0 ] ,而 不只是 0 。
3.4. 带闭包的嵌套 Let¶
下面的随机化问题着重考察带闭包的嵌套 let 表达式 的求值。连续 三次正确解答即可获得学分。
同样,提交答案时请记住给出完整的指称值。
