乐趣区

声明式编程

修改 LaTeX 调试中,还未翻译完成,如果你看到了这篇文章,请当天晚些时候再来看看吧
第二部分的导言
在本书的第一部分我曾说范畴论和编程都与可复合性相关。在编程时,你总会不断地把问题分解到一个你能处理其细节的程度,然后一个个地解决每个子问题,最后把它们自底向上地重新组合起来。这里,大致来说,有两种实现的方法:告诉计算机要做什么(what to do),或者告诉它如何去做 (how to do it)。也就分别是声明式(编程)和命令式(编程)。
你甚至能够从最底层的地方来理解这件事。复合本身可以被声明式地定义,例如 h 是 f 与 g 的一个复合:
h = g . f
或者命令式地定义,这时,先调用 f,保留计算结果,再对该结果调用 g:
h x = let y = f x
in g y
一个程序的这种命令式版本通常被描述成按照时间顺序进行的一个指令序列。尤其是,对 g 的调用不能发生在 f 执行完成之前。至少这是一种概念上的想象————在一个参数传递方式为按需调用的惰性语言中,实际的执行顺序可能完全不同。
实际上,声明式代码和命令式代码的执行过程只会有一点甚至没有差异,这取决于编译器有多聪明。但这两种方法论毕竟不同,尤其在我们寻找问题的解决方案时和考虑代码的可维护性和可调试性时,它们会不一样的彻彻底底。
这里有一个重大问题:当面对一个具体问题时,我们是否总是可以在声明式和命令式的方法中选择一个?进一步,如果有一个声明式的的解决方案,是否一定可以转化为计算机代码?这个问题的答案远非显然,并且,如果我们能够找到这个问题的答案,我们对宇宙的理解可能就会迎来一场革命。
让我来详细说说。这个问题在物理中有一个类似的对应,它要么会是一些潜在的深刻原则的一个重要部分,要么会告诉我们一些有关大脑如何工作的事。理查德·费曼曾经提到,这个对应启发了他在量子电动力学领域的工作。
大部分的物理定律有两种表达形式。一种使用局部(local)的观念,或者说无穷小(infinitesimal)和分析(considerations)。我们会在一个小领域内观察系统的状态,并且预测它在下一时刻如何演变。这种观念通常用一组的微分方程组的形式表达,并且我们会在一个周期时间里对它们做积分或求和。
让我们看看这种形式和命令式思维有多像:我们通过一系列的“小碎步”达到最终解,而每一个碎步取决于前一步的结果。事实上,物理系统的计算机仿真也是按部就班地把微分方程组重写为差分方程组,然后迭代它们。这也是行星游戏中宇宙飞船的运动方式。在每一个时间步长里,飞船的位置通过一个小的增量改变,它就是速度乘以时间间隔。而速度呢,也是如此迭代计算,它的小增量正比于加速度,也就是力除以质量。

牛顿运动定律所对应的微分方程组有明确的写法:
F = m dv/dt
v = dx/dt
同样的方法可以用来处理更复杂的问题,比如用麦克斯韦方程组来描述电磁场的传播,或者甚至是用格点量子色动力学来描述一个质子中的夸克和胶子的行为。
这种局部思维方式与离散的时空有关,而史蒂芬·沃尔夫勒姆的将整个宇宙的复杂度约减为一个元胞自动机系统的伟大构想就是用数字计算机计算这些离散的时空。
另一种是全局的方法。我们观察系统的初始状态和终止状态,然后通过最小化某个函数计算出两者之间的轨迹。最简单的例子就是费马的最小时间原理。它声称光线会沿着时间最小的路径传播。特别地,当没有反射物或折射物时,光线会沿着最短的路径传播,也就是一条直线。但是,光在厚实(透明)的材料中会传播的更慢,比如水或玻璃。所以如果你选择的起点在空气中,终点在水里,那么光就会在空气中传播更长一点以获得水中路线的缩减。这个最小时间所对应的路径使得光在空气和水的界面处发生折射,导出乐斯涅尔折射定律:$$ \sin \theta_1 / \sin \theta_2 = v_1 / v_2 $$ 其中,$$ v_1 $$ 是光在空气中的传播速度

退出移动版