什么是函数

函数,在数学中被定义为一个【映射】。比如说这本Abstract Algebra就是这样定义函数的:

1

但这个定义并没有告诉我们【如何得到函数值】。事实上,从小到大,我们所接触的函数都是这个样子的:

f(x)=x2f(x)=x^2

可见,【如何得到函数值】是通过f(x)=exprf(x) = expr这个记法给出的,它的含义是:

如果aa 是一个值,那么f(a)f(a) 的含义是,将exprexpr中的xxaa替换。

比如说,对于f(x)=x2f(x) = x^211是一个值,那么f(1)f(1)的含义是,将x2x^2中的xx11替换,这样会得到121^2,也就是11

在这个定义下,【得到函数值的方法】就是【替换】。我们用

[1/x]x2[1/x]x^2

来表示【把x2x^2中的xx替换为11】。

显然地,一个【得到函数值的方法】唯一地确定了一个函数,那么函数本身就可以用【得到函数值的方法】表示。这也就是我们日常把f(x)f(x)看作一个函数的理论基础。

真的需要【f】吗?

在刚才的定义中,我们发现,一个【得到函数值的方法】,只需要给出两个信息:

  1. 将要进行替换的表达式
  2. 将要替换的符号

在这个过程中,我们丝毫没有看到ff这个符号有什么作用。实际上,ff只是一个代称,它【代表】我们刚才所说的函数。

那么,【函数的计算方法】的真正表示形式应该类似于:

xexprx \rightarrow expr

(其中exprexpr表示一个表达式,下同。)

或者

x,exprx,expr

再或者

λx.expr\lambda x.expr

最后一种就是大名鼎鼎的lambda表达式。实际上,f(x)f(x)记法只是一个语法糖:

f(x)=exprf(x) = expr

表示:

f:λx.exprf : \lambda x.expr

在传统的lambda表达式中,【调用函数】用(fx)(f x)表示,其实也可以用f(x)f(x)表示,下面不做区分。

f(x)记法的滥用

我们已经看到,f(x)=exprf(x)=expr只是lambda表达式定义的语法糖,一般来说,语法糖不会引入什么特别恶劣的问题,所以似乎也无伤大雅,也不值得我写一篇文章来专门批判了。

问题在于,编程语言中的语法糖,确实不会引入什么特别恶劣的问题;但数学语言中的语法糖就不一定了。其原因在于,(逻辑学家以外的)数学家们从未对符号的严谨有过追求,他们追求的是【自己思考】的严谨,这导致数学语言是一种非常不严谨的语言,如果随意引入语法糖,可能会产生一些很麻烦的问题。

f(x)f(x)的引入究竟产生了什么问题呢?简单来说,就是f(x)f(x)到底是什么的问题。

奇怪,f(x)f(x)不是已经被看成是一个语法糖了吗?为什么说f(x)f(x)本身是什么还是一个【问题】呢?

请读者再次看看本节开头的那句话:

f(x)=exprf(x)=expr只是lambda表达式定义的语法糖

这句话并没有指出单独书写f(x)f(x)到底是什么意思。

假设

f(x)=x2f(x) = x^2

那么,f(1)f(1)表示[1/x]x2[1/x]x^2,但是f(x)f(x),这个符号本身到底表示什么呢?

如果直接套用之前的表示,我们会得到:

[x/x]x2=x2[x/x]x^2 = x^2

问题在于,x2x^2这个式子的xx本身是没有任何定义的,所以x2x^2这个式子本身就不是一个函数,而是一个非法表达式,进而f(x)f(x)也是非法表达式

哈哈,如果我说数学家们每天都在用的东西竟然是【非法表达式】,他们肯定要气得跳脚,马上出来辩解说

f(x)f(x)表示一个【函数】

好,我们先接受这个定义,在我们的符号体系中加入一条新的规则:

f(x)f(x)表示一个函数,如果ff没有定义,那么f(x)f(x)表达任意的以xx为参数的函数,如果f(x)f(x)有定义,那么f(x)f(x)表示之前定义的那个函数。

那么问题又来了,请问如果

f(x)=x2f(x) = x^2

f(t1)f(t-1)表示什么呢?

这并不是什么理论问题,而是《五年高考三年模拟》《信号与系统》这两本书里经常出现的东西,比如说:

1

这里的uu被定义为:

u(x)={1,x>00,u(x)=\left\{ \begin{aligned} &1,x>0 \\ &0,其他 \end{aligned} \right.

按照一般的理解,在这个图像中,τ\tau表示【自变量】,tt表示一个提前给定好的值。所谓的自变量,毫无疑问就是我们之前一直在说的【参数】,或者说lambda表达式里的【绑定变量】。说到这里,聪明一些的读者就会明白,u(tτ)u(t-\tau)这个式子的真正含义是:

λτ.u(tτ)tR\lambda \tau.u(t-\tau) \\ t \in R

我们刚刚加入了一条关于f(x)f(x)含义的规则,但这条规则并不能解释类似于f(x1)f(x-1)这样的式子究竟是什么意思,我们还要加入新的规则:

如果exprexpr中,除【主变量】以外的符号的定义均已经给出,那么f(expr)f(expr)就是一个以exprexpr中的【主变量】为参数的新函数,不失一般性地表示为λx.f(expr)\lambda x.f(expr)

在加入了这个规则以后,大部分关于f(x)f(x)的记法就都能被解释了,而且,之前确立的规则也可以被扔掉,因为在这个定义下,

f(x)λx.f(x)f(x) \equiv \lambda x.f(x)

是显然的,而且只要你不研究什么【延迟求值】之类的问题,f(x)f(x)λx.f(x)\lambda x.f(x)是没什么区别的。

但某些数学家对符号的随意使用导致的问题不是这个补丁能够补上的,请再次翻开《信号与系统》,仔细研究其中的【卷积】:

2

在这个式子中出现了四个tt,它们分别是:

  1. y(t)y(t)中的tt
  2. h(tτ)h(t-\tau)中的tt
  3. x(t)x(t)中的tt
  4. h(t)h(t)中的tt

问题来了,请问这四个tt是同一个tt吗?

如果你的《信号与系统》学得很好,那么你应该认为1.和2.中的tt是同一个tt,除此之外都是不同的tt

如果你这样认为,那么把它写成无歧义式就应该是这样的:

y(t)=x(τ)h(tτ)dτy=xhy(t)=\int_{-\infty}^{\infty}x(\tau)h(t-\tau)d\tau \\ y = x * h

下面的式子也可以写成:

λt.y(t)=(λt.x(t))(λt.h(t))\lambda t. y(t) = (\lambda t.x(t)) * (\lambda t.h(t))

改写成无歧义式以后,这个式子的含义是很确定的。我们看到,真正令人迷惑的,其实是这个式子:

y(t)=x(t)h(t)y(t) = x(t) * h(t)

在这里,*这个东西是【卷积】的意思,它是两个函数的运算,但是,由于我们一般习惯于把$y(t) = expr 看作\lambda t.expr$,上面的式子可以被看作:

y:λt.x(t)h(t)y: \lambda t.x(t) * h(t)

发现问题在哪里了吗?如果x(t)=t2x(t) = t^2h(t)=t3h(t)=t^3的话,我们立刻得到:

y:λt.t2t3y: \lambda t.t^2 * t^3

现在求一下y(1)y(1),也就是:

[1/t](t2t3)=11[1/t](t^2 * t^3) = 1 * 1

天哪!这个理解竟然会导致11卷积11这种式子的出现,再次说一句,卷积是两个函数的运算,不是数的运算,这理解根本就是错误的。

至此为止,我们终于发现了真正的二义性之所在:

f(x)f(x)既可以表示一个【函数调用后的返回值】,又可以表示【一个函数】

拥抱匿名函数

我们之前已经看到,【匿名函数】才是【作为计算的函数】的本质表达。使用匿名函数,某些需要文字叙述的事实可以一下子变得形式化起来。

比如《信号与系统》中的【时不变系统】,我的老师用这样的形式来表达:

3

其实,这完全是符号设计错误的结果。系统本质上就与输入函数xx无关,应该把它放在参数位置上:

output:λx,t.(...)output:\lambda x,t.(...)

这样一来,最简单的系统就可以被表示为:

λx,t.x(t)\lambda x,t.x(t)

什么是【时不变系统】呢,一个系统ss是时不变系统,就是说:

t,t0,s(x,tt0)s(λt.x(tt0),t)\forall t,t_0,s(x,t-t_0) \equiv s(\lambda t.x(t-t_0), t)

这里的λx,t\lambda x,t相当于一个【二元函数】,它接收两个参数。当然,这不是传统的lambda表达式的写法。

Church在他提出lambda表达式的论文中,其实只是引入了一些逻辑符号,想让逻辑描述不需要自然语言。这和我们今天的套路不谋而合,但,lambda表达式的魔力远远不止于此。其实,随着lambda的出现,一段新的历史已经开始了……