语法糖是什么呢?按我现在的理解,如果一门语言没有某个语法,照样可以通过其它更通用的方式来表达某种语义。这种语法的引入,只不过是 让表达语义更方便了。那么这个语法,就叫语法糖。
Scheme 中有两个关键字 lambda, let ,我目前的理解是,他们就是语法糖。
lambda
lambda 用来定义一个匿名函数。有时候,一个函数我们只在一个地方用一次,完全没必要给它定义一个名字,这时候,我们就用 lambda 定义 一个匿名函数。
如果没有 lambda ,那么我们定义函数时,会使用下面这种方式:
(define (pi-sum a b)
(define (pi-term x)
(/ 1.0 (* x (+ x 2))))
(define (pi-next x)
(+ x 4))
(sum pi-term a pi-next b))
其中像 pi-term 和 pi-next 这样的小函数,完全可以通过 lambda 定义一个匿名函数。
(define (pi-sum a b)
(sum
(lambda (x) (/ 1.0 (* x (+ x 2))))
a
(lambda (x) (+ x 4))
b))
let
let 用来定义局部变量,其实没有局部变量,我们也可以表达相应的语义,只不过有了 let ,定义起来更方便了。由于之前没有函数式语言 编程的经验,完全不知道没有局部变量要怎么办,所以还是看一个例子。
计算
f(x,y) = x(1+xy)^2 + y(1-y) + (1+xy)(1-y)
我们可以通过
a = 1 + xy
b = 1 - y
f(x, y) = xa^2 + yb + ab
来定义这个函数,这里面的 a, b 就相当于局部变量,那么在没有局部变量语法的情况下,我们怎么定义这个函数呢?
办法就是定义一个内部函数,将形式参数设定为 a, b,然后将 1 + xy
和 1-y
作为实际参数传递进去:
(define (f x y)
(define (f-helper a b)
(+ (* x (square a))
(* y b)
(* a b)))
(f-helper (+ 1 (* x y))
(- 1 y)))
而如果引入 let 这个语法糖后,定义起来就变得直观,容易多了:
(define (f x y)
(let ((a (+ 1 (* x y)))
(b (- 1 y)))
(+ (* x (square a))
(* y b)
(* a b))))
从这里可以看出,let 作用域内分两部分,一部分定义局部变量,另一部分是函数体。
通过对 SICP 这部分的学习,我明白了,什么是本质上的东西,什么是语法糖。本质上的东西是对函数及求值方式的定义,他们形式简单,功能强大, 大部分语义都可以通过这些定义出来。只是为了方便,我们引入了语法糖,来更容易地表达语义。
关于 lambda 这个词的来源,书中也有介绍,这是历史原因,源于 Alonzo Church 的 lambda 演算。lambda 演算为研究函数定义, 函数应用提供了坚实的数学基础。我还隐约记得在程序设计语言课上,老师教我们用 lambda 演算定义自然数,加法等运算法则,true/false, if/else 等等。你可以看出,这些本质的东西,可以表达非常非常多的东西,功能异常强大。
注:这是我学习 SICP 后的总结与思考,例子也都来自于此书。