函数式编程能够以声明式风格编写库来解决存在的问题,我们已经看到用 LINQ 处理数据,用WPF 处理用户界面;但在函数式编程中,通常是创建库来解决自己的问域。
我们前面提到过,声明式风格可以不考虑实现的细节,遗漏了一些东西。函数式编程没有任何神秘的力量,能为我们实现困难的部分。设计我们自己的库时,需要实现所有的技术细节;只是实现的细节隐藏在库中(就像 LINQ 隐藏了所有的复杂性一样),这样,我们就能一劳永逸地解决普遍存在的问题。
在第十五章,我们将开发一个动画库,清单 1.6 以声明方式使用这个库创建动画。你不一定完全理解代码,只看一下使用声明式风格带来的好处。在某种意义上,它是类似于 WPF,它描述了动画应该怎么样,而不是如何使用计时器来绘制。
Listing 1.6 Creating functional animation (C#)
var greenCircle = Anims.Circle(
Time.Forever(Brushes.OliveDrab),100.0f.Forever());
var blueCircle = Anims.Circle(
Time.Forever(Brushes.SteelBlue),100.0f.Forever());
var movingPoint =Time.Wiggle * 100.0f.Forever(); [1]
var greenMoving =greenCircle.Translate(movingPoint, 0.0f.Forever()); [2]
var blueMoving =blueCircle.Translate(0.0f.Forever(), movingPoint);
var animation =Anims.Compose(greenMoving, blueMoving); [3]
我们将在第十五章中详细解释具体内容。你可能已经猜到,这个动画创建两个椭圆:一个绿色的,一个蓝色的。以后,它使用 Translate 方法[2]改变动画中椭圆的位置,使用 Compose 方法[3]把两个椭圆组合成一个动画(用 animation 值表示)。在窗体上渲染此动画,就会得到图 1.1 所示的结果。
图 1.1 右侧的椭圆是从左到右移动,左边的椭圆是从上到下移动。
整个声明式的描述是基于动画值。有一个基本的动画值Time.Wiggle,其值在 -1 到 +1 之间波动;另一个基本构造x.Forever(),创建动画值,总有相同的值。如果把 Wiggle 乘以 100,就得到范围介于-100 到 +100 之间的动画值,这些动画值可以用于指定图形对象,比如两个椭圆的动画。图 1.1 显示了绿色的 X 坐标和蓝色的 Y 坐标值是大约 + 100 的状态。
在清单 1.6 中,我们不必知道任何表现动画值的内容,因为是通过用基本动画值的计算来描述整个动画的。从代码中还可以看到声明式风格的另一个方面,动画原则上是用一个表达式来描述的。为了使代码更具可读性,我们声明了几个局部变量,如果用变量的初始化代码来代替,也一样。
组合
声明式库的一个重要特点,就是能够以组合方式使用。在清单 1.6 所示的动画库中,就能看出来。我们可以用几个基元,如 Time.Wiggle 和 x.Forever() 就能生成多个动画值,如 movingPoint。同样,通过应用操作,如 Translate 或 Anim.Compose,可以把简单的动画图形对象组合起来,形成新的动画。另一个例子是使用 LINQ,可以把一个复杂查询的一部分提取出来,形成单独的查询,实现重用。我们可以构建自己的基元(假设绕行的轨道),并使用它们来构建动画(例如,太阳系)。
在前面,我们学习了声明式编程,这将是我们使用函数语言编程的主要方法。清单 1.6 显示这种风格如何用高级库函数来描述动画。在下一节,我们会将注意力转移到技术性更强,也更有意义的函数式特征:不可变性(immutability)。
1.4.1.3 声明式函数动画