显然, 我们需要一种记号来表示过程. Scheme中的这个记号一般被称为lambda表达式, 它的式样与名字, 自然是源于lambda calculus.
lambda表达式的一般句法是(lambda (<var>*) <exp>). 使用尖括号括起来的符号表示一个句法范畴, 当一个句法里出现句法范畴时, 它意在被该句法范畴的一个元素 (或者说一个实例) 所替代. 这里我们用<var>表示所有符号构成的句法范畴, <exp>表示的是所有表达式构成的句法范畴. 虽然表达式是什么样的东西我们还没有定义, 但直到我们开始讨论魔法语言学之前, 我们都认为这个概念从直觉上是可以理解的. *是Kleene star, 它跟在一个句法范畴后面, 表示零个至任意有限多个皆可.
以上的说明有些复杂, 还是举简单的例子来得容易.
我们可以看到, 对于(lambda (x) (* x x))求值的结果是一个过程. 应用这个过程, 它的句法与之前我们见过的完全一致, 比如, 于分数1/2上, 就得到了1/4. 显然, 这个过程的意思是平方, 任何学过数学的人都能明白, x是过程的(形式)参数, (* x x)是过程的体. (之所以称这个参数是形式的, 是因为它只是一个占位符, 不具有实际的含义.)
这个lambda表达式的数学记号的对应物是
不过, 或许"令f(x)=x*x"是人们更熟悉的说法, 但是读者应该注意到, 函数不需要拥有一个名字, 这是它外在的东西.
这个记号的优点在于用它表示泛函 (functional, 这里我们指以函数为输入或输出的函数) 时, 不会显得凌乱, 反而特别规整, 比如看以下这个例子.
这个例子乍看上去非常复杂, 但是实际上非常简单, 所以我们要分成几个步骤看待它.
首先, 第一个值得注意的部分是
它先接受两个参数f和g, 这两个参数的名字实际上已经暗示了它们应该是函数, 然后返回一个函数, 它接受一个参数x, 返回(f (g x)). 对数学略知一二的读者想必已经明白这个过程表达的是函数的复合之意.
接着, 将函数的复合应用于两个实际的函数, 一个是平方, 一个是加一.
这个函数应用的结果是一个函数, 它先给参数加上一, 接着再进行平方操作.
最后, 我们将这个函数应用于实际的参数3.
就得到了结果16.
这种情况确实括号很乱。。。。写得更数学一点的话会是这样:
(λf λg λx f g x) (λx * x x) (λx + x 1) 3
这样就能更容易得看出来f是x => x*x, g是x => x + 1,所以整个式子用js转写就是
(x => x*x)((x => x+1)(3))
Welcome to Node.js v16.15.0.
Type ".help" for more information.
> (x => x*x)((x => x+1)(3))
16
当然这段代码在js里等价的写法是
((f,g,x)=>f(g(x)))(x=>x*x, x=>x+1, 3)
括号也很乱
如果用更数学一点的转写的话,
(f=>g=>x=>f(g(x)))(x=>x*x)(x=>x+1)(3)
不过还是逃不掉括号