基于函数式编程思想的有限元编程(二)
基于函数式编程思想的有限元编程(一)本文主要探讨纯函数与不可变。要将一个函数定义成纯函数,当且仅当:• 输入相同时,输出始终相同• 没有副作用C++语言有一个经典的交换函数swap void swap(int& a, int& b){ int temp = a; a = b; b = temp;}int main(){ int a = 5; int b = 10, swap(a, b); // 现在a的值为10,b的值为5 return 0;} 当该函数修改函数体以外的数据,或者修改函数的输入时,就会产生副作用。Python定义一个纯函数 def func_pure(p,q): u = q['x'] - p['x'] v = q['y'] - p['y'] return{'u':u,'v':v}point1 = {'x':10,'y':20}point2 = {'x':12,'y':20}func_pure(point1,point2) 给定相同的输入点point1和point2,返回值总是相同,并且在函数体之外的任何东西都没有被修改。相比之下,以下代码是“不纯”版本: last_point = {'x':10,'y':20}def func_impure(q): u = q['x'] - last_point['x'] v = q['y'] - last_point['y'] new_vector = {'u':u,'v':v} last_point = q return new_vector 这个代码使用last_point的共享状态,该状态在每次调用时都会改变。这种改变是该函数的一个副作用。函数返回的向量依赖于last_point的共享状态,因此对相同的输入点,该函数不会返回相同的向量。这种现象在多线程编程很常见。上面的讨论对不可变有了初步印象,如果某样东西不随时间变化那么它就是不可变的。如果决定以函数式编程的方式编写代码,我们必须避免可变数据,使用纯函数对程序进行建模。假设,我们使用字典在平面上定义了一个点P和向量v,如果想计算P沿着向量移动后所生成的点,我们可以用函数式编程的方式,用函数创建一个新点。示例如下: def displaced_point(point,vector): x = point['x'] + vector['u'] y = point['y'] + vector['v'] return{'x':x, 'y':y}point = {'x':10,'y':20}vector = {'x':12,'y':20}new_point = displaced_point(point,vector) 这个函数是纯函数。给定相同的点和向量作为输入参数,得到的位移点总是相同,而且函数处理的数据没有任何改变,也包括函数参数。与之相反,非函数式编程的解决方法可能需要使用如下函数来改变原来的点: def displaced_point(point,vector): point['x'] += vector['u'] point['y'] += vector['v'] 这个函数修改了作为参数输入的point,违反了函数式编程的关键规则。函数式风格的一个重要优点是,通过恪守数据结构的不可变性,我们可以避免意料之外的副作用。当修改某个对象时,你可能并不知道代码中引用该对象的所有位置。如果有其他部分代码依赖于该对象的状态,就可能出现难以预料的副作用。因此,在对象发生改变之后,程序的行为可能与预期的不同。这类错误非常难发现,甚至可能需要数小时的调试。在项目中尽量减少可变对象的数量,可以使其更可靠,更不容易出错。 来源:数值分析与有限元编程