函数式编程

函数式编程的优点

            在函数式编程中,由于数据全部都是不可变的,所以没有并发编程的问题,是多线程安全的。可以有效降低程序运行中所产生的副作用,对于快速迭代的项目来说,函数式编程可以实现函数与函数之间的热切换而不用担心数据的问题,因为它是以函数作为最小单位的,只要函数与函数之间的关系正确即可保证结果的正确性。

            函数式编程的表达方式更加符合人类日常生活中的语法,代码可读性更强。实现同样的功能函数式编程所需要的代码比面向对象编程要少很多,代码更加简洁明晰。函数式编程广泛运用于科学研究中,因为在科研中对于代码的工程化要求比较低,写起来更加简单,所以使用函数式编程开发的速度比用面向对象要高很多,如果是对开发速度要求较高但是对运行资源要求较低同时对速度要求较低的场景下使用函数式会更加高效。

 

函数式编程的缺点

            由于所有的数据都是不可变的,所以所有的变量在程序运行期间都是一直存在的,非常占用运行资源。同时由于函数式的先天性设计导致性能一直不够。虽然现代的函数式编程语言使用了很多技巧比如惰性计算等来优化运行速度,但是始终无法与面向对象的程序相比,当然面向对象程序的速度也不够快。

            函数式编程虽然已经诞生了很多年,但是至今为止在工程上想要大规模使用函数式编程仍然有很多待解决的问题,尤其是对于规模比较大的工程而言。如果对函数式编程的理解不够深刻就会导致跟面相对象一样晦涩难懂的局面

 

假设现在我们需要开发一个绘制数学函数平面图像(一元)的工具库,可以提供绘制各种函数图形的功能,比如直线f(x)=ax+b、抛物线f(x)=ax²+bx+c或者三角函数f(x)=asinx+b等等。那么怎么设计公开接口呢?由于每种行数的系数(a、b、c等)不同,并且函数构造也不同。正常情况下我们很难提供一个统一的接口。所以会出现类似下面这样的公开方法:

//绘制直线函数图像
public void DrawLine(double a, double b)
{
    List<PointF> points = new List<PointF>();
    for(double x=-10;x<=10;x=x+0.1)
    {
        PointF p =new PointF(x,a*x+b);
        points.Add(p);
    }
    //将points点连接起来
}
//绘制抛物线图像
public void DrawParabola(double a, double b, double c)
{
    List<PointF> points = new List<PointF>();
    for(double x=-10;x<=10;x=x+0.1)
    {
        PointF p =new PointF(x,a*Math.Pow(x,2) + b*x + c);
        points.Add(p);
    }
    //将points点连接起来
}
...
DrawLine(3, 4);   //绘制直线
DrawParabola(1, 2, 3);    //绘制抛物线

 

如果像上面这种方式着手的话,绘制N种不同函数就需要定义N个接口。很明显不可能这样去做。

如果我们换一种方式去思考,既然是给函数绘制图像,为什么要将它们的系数作为参数传递而不直接将函数作为参数传给接口呢?是的,没错,要绘制什么函数图像,那么我们直接将该函数作为参数传递给接口。由于C#中委托就是对方法(函数,这里姑且不讨论两者的区别)的一个封装,那么C#中使用委托实现如下:

public delegate double Function2BeDrawed(double x);
//绘制函数图像
public void DrawFunction(Function2BeDrawed func)
{
    List<PointF> points = new List<PointF>();
    for(double x=-10;x<=10;x=x+0.1)
    {
        PointF p =new PointF(x,func(x));
        points.Add(p);
    }
    //将points点连接起来
}
...
Function2BeDrawed func = 
    (Function2BeDrawed)((x) => { return 3*x + 4;}); //创建直线函数
DrawFunction(func);  //绘制系数为3、4的直线
Function2BeDrawed func2 =
    (Function2BeDrawed)((x) => {return 1*Math.Pow(x,2) + 2*x + 3;}); //创建抛物线函数
DrawFunction(func2);  //绘制系数为1、2、3的抛物线
Function2BeDrawed func3 = 
    (Function2BeDrawed)((x) => {return 3*Math.Sin(x) + 4;}); //创建正弦函数
DrawFunction(func3);  //绘制系数为3、4的正弦函数图像

 

如上。将函数(委托封装)作为参数直接传递给接口,那么接口就可以统一。至于到底绘制的是什么函数,完全由我们在接口外部自己确定。

将函数看作和普通类型一样,可以对它赋值、存储、作为参数传递甚至作为返回值返回,这种思想是函数式编程中最重要的宗旨之一。

posted @ 2019-03-28 15:07  小鱼2015  阅读(133)  评论(0)    收藏  举报