目录

  • 一个问题
  • 函数式编程中的函数
  • 数学与函数式编程
  • 混合式编程风格

一个问题

假设现在我们需要开发一个绘制数学函数平面图像(一元)的工具库,可以提供绘制各种函数图形的功能,比如直线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个接口。很明显不可能这样去做。

(注,如果采用虚方法的方式,要绘制N种不同函数图像就需要定义N个类,每个类中都需要重写生成points的算法)

如果我们换一种方式去思考,既然是给函数绘制图像,为什么要将它们的系数作为参数传递而不直接将函数作为参数传给接口呢?是的,没错,要绘制什么函数图像,那么我们直接将该函数作为参数传递给接口。由于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的正弦函数图像

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

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

注:上面代码中,如果觉得创建委托对象的代码比较繁杂,我们可以自己再定义一个函数接收a、b两个参数,返回一个直线函数,这样一来,创建委托的代码就不用重复编写。

函数式编程中的函数

在函数式编程中,我们将函数也当作一种类型,和其他普通类型(int,string)一样,函数类型可以赋值、存储、作为参数传递甚至可以作为另外一个函数的返回值。下面分别以C#和F#为例简要说明:

注:F#是.NET平台中的一种以函数式编程范式为侧重点的编程语言。举例中的代码非常简单,没学过F#的人也能轻松看懂。F#入门看这里:MSDN

定义:

在C#中,我们定义一个整型变量如下:

int x = 1;

在F#中,我们定义一个函数如下:

let func x y = x + y

赋值:

在C#中,我们将一个整型变量赋值给另外一个变量:

int x = 1;
int y = x;

在F#中,我们照样可以将函数赋值给一个变量:

let func = fun x y -> x + y  //lambda表达式
let func2 = func

存储:

在C#中,我们可以将整型变量存储在数组中:

int[] ints = new int[]{1, 2, 3, 4, 5};

在F#中,我们照样可以类似的存储函数:

let func x = x + 1
let func2 x = x * x
let func3 = fun x -> x - 1    //lambda表达式
let funcs = [func; func2; func3]  //存入列表,注意存入列表的函数签名要一致

传参:

在C#中将整型数值作为参数传递给函数:

void func(int a, int b)
{
    //
}
func(1, 2);

在F#中将函数作为参数传递给另外一个函数:

let func x = x * x  //定义函数func
let func2 f x =   //定义函数func2 第一个参数是一个函数
   f x
func2 func 100   //将func和100作为参数 调用func2

作为返回值:

在C#中,一个函数返回一个整型:

int func(int x)
{
    return x + 100;
}
int result = func(1);  //result为101

在F#中,一个函数返回另外一个函数:

let func x =
   let func2 = fun y -> x + y
   func2             //将函数func2作为返回值
let result = (func 100) 1  //result为101,括号可以去掉

数学和函数式编程

函数式编程由Lambda演算得来,因此它与我们学过的数学非常类似。在学习函数式编程之前,我们最好忘记之前头脑中的一些编程思想(如学习C C++的时候),因为前后两个编程思维完全不同。下面分别举例来说明函数式编程中的一些概念和数学中对应概念关系:

注:关于函数式编程的特性(features)网上总结有很多,可以在这篇博客中看到。

1.函数定义

数学中要求函数必须有自变量和因变量,所以在函数式编程中,每个函数必须有输入参数和返回值。你可以看到F#中的函数不需要显示地使用关键字return去返回某个值。所以,那些只有输入参数没有返回值、只有返回值没有输入参数或者两者都没有的函数在纯函数式编程中是不存在的。

2.无副作用

数学中对函数的定义有:对于确定的自变量,有且仅有一个因变量与之对应。言外之意就是,只要输入不变,那么输出一定固定不变。函数式编程中的函数也符合该规律,函数的执行既不影响外界也不会被外界影响,只要参数不变,返回值一定不变。

3.柯里化

函数式编程中,可以将包含了多个参数的函数转换成多个包含一个参数的函数。比如对于下面的函数:

let func x y = x + y
let result = func 1 2  //result为3

可以转换成

let func x =
   let func2 = fun y -> x + y
   func2
let result = (func 1) 2   //result结果也为3,可以去掉括号

可以看到,一个包含两个参数的函数经过转换,变成了只包含一个参数的函数,并且该函数返回另外一个接收一个参数的函数。最后调用结果不变。这样做的好处便是:讲一个复杂的函数可以分解成多个简单函数,并且函数调用时可以逐步进行。

其实同理,在数学中也有类似“柯里化”的东西。当我们计算f(x,y) = x + y这个函数时,我们可以先将x=1带入函数,得到的结果为f(1,y) = 1 + y。这个结果显然是一个关于y的函数,之后我们再将y=2带入得到的函数中,结果为f(1,2) = 1 + 2。这个分步计算的过程其实就是类似于函数式编程中的“柯里化”。

4.不可变性

数学中我们用符号去表示一个值或者表达式,比如“令x=1”,那么x就代表1,之后不能再改变。同理,在纯函数式编程中,不存在“变量”的概念,也没有“赋值”这一说,所有我们之前称之为“变量”的东西都是标识符,它仅仅是一个符号,让它表示一个东西之后不能再改变了。

5.高阶函数

在函数式编程中,将参数为函数、或者返回值为函数的这类函数统称之为“高阶函数”,前面已经举过这样的例子。在数学中,对一个函数求导函数的过程,其实就是高阶函数,原函数经过求导变换后,得到导函数,那么原函数便是输入参数,导函数便是返回值。

混合式编程风格

过程式、面向对象再到这篇文章讲到的函数式等,这些都是不同地编程范式。每种范式都有自己的主导编程思想,也就是对待同一个问题思考方式都会不同。很明显,学会多种范式的编程语言对我们思维方式有非常大的好处。

无论是本文中举例使用到的F#还是Java平台中的Scala,大多数冠名“函数式编程语言”的计算机语言都并不是纯函数式语言,而是以“函数式”为侧重点,同时兼顾其他编程范式。就连曾经主打“面向对象”的C#和Java,现如今也慢慢引入了“函数式编程风格”。C#中的委托、匿名方法以及lambda表达式等等这些,都让我们在C#中进行函数式编程成为可能。如果需要遍历集合找出符合条件的对象,我们以前这样去做:

foreach(Person p in list)
{
    if(p.Age > 25)
    {
        //...
    }
}

现在可以这样:

list.Where(p => p.Age>25).Select(p => p.Name).toArray();

本篇文章开头提出的问题,采用C#委托的方式去解决,其实本质上也是函数式思想。由于C#必须遵循OO准则,所以引入委托帮助我们像函数式编程那样去操作每个函数(方法)。

本篇文章介绍有限,并没有充分说明函数式编程的优点,比如它的不可变特性无副作用等有利于并行运算、表达方式更利于人的思维等等。实质上博主本人并没有参与过实际的采用函数式语言开发的项目,但是博主认为函数式思想值得我们每个人去了解、掌握。(本文代码手敲未验证,如有拼写错误见谅)

作者:周见智 
出处:http://www.cnblogs.com/xiaozhi_5638/ 
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

本文转自周见智博客博客园博客,原文链接:http://www.cnblogs.com/xiaozhi_5638/p/4762846.html,如需转载请自行联系原作者

每个人都应该懂点函数式编程相关推荐

  1. 红米手机计算机的隐藏功能,红米手机按键隐藏神功能,米粉很多人都不全懂!...

    原标题:红米手机按键隐藏神功能,米粉很多人都不全懂! 红米手机以实用而备受用户的喜爱,当然红米手机隐藏许多鲜为人知的快捷键,你知道多少呢?下面就为大家带来红米手机快捷键小技巧,非常实用哦! 一.按健功 ...

  2. 每个人都应该懂点攻防

    网络如此发达的今天,攻击无处不在,而每一次攻击的成功往往造成信息的泄漏,严重者甚至带来经济的损失.所以作为技术人员,我们每个人都应该懂一点攻防:这样做虽然无法做到完全保护个人隐私,但对增强个人安全意识 ...

  3. 99%的人都不知道,女孩子学编程居然有这么多好处!

    不知道大家有没有看 前阵子的<最强大脑之燃烧吧,大脑> 里面有一位被保送清华大学的特殊女孩. 一亮相就惊艳全场 不仅颜值高到爆表 关键是还有才 六项机器人国家专利,直接保送清华. 她就是1 ...

  4. 崩溃!补税10W+ —— 每个人都应该懂的个税计算方法

    点击????小卡片,回复 "1024" 获取大厂面试指南 背景 大家好,我是石头哥. 前一阵公司刚发了年终奖了,同事们也都在讨论年终奖.股票等扣税的事情. 年终奖17W,扣税4W+ ...

  5. 每个人都该懂点的版本管理技能

    引言 作为一个程序员,每天除了和写代码打交道,还涉及到项目代码的版本控制.借助 svn 和 git 这两大版本控制系统,我们能够比较好地控制项目版本.因为每天都从中受益,所以在此安利给大家,以下的内容 ...

  6. 云函数与函数式编程思想结合会产生什么?

    导语   函数式编程是一种编程范式,也就是如何编写程序的方法论.随着函数式编程被更多人广泛的关注.很多古老的函数式编程语言都重获新生,就连Java这样的老牌的编程语言都开始往函数式编程的方式开始靠近. ...

  7. 为什么说编程和英语是每个人都必须掌握的技能?

    我们今天的主题要从奥巴马的一段演讲开始说起. 这段视频是奥巴马2014年应邀为美国在线编程教育网站code.org举办的"编程一小时"活动做的宣传片.为了呼吁美帝人民学习编程知识, ...

  8. 多角度让你彻底明白yield语法糖的用法和原理及在C#函数式编程中的作用

    如果大家读过dapper源码,你会发现这内部有很多方法都用到了yield关键词,那yield到底是用来干嘛的,能不能拿掉,拿掉与不拿掉有多大的差别,首先上一段dapper中精简后的Query方法,先让 ...

  9. 你真的理解函数式编程吗?

    大数据以及人工智能越来越流程,你是否可以轻松适应大数据编程,函数式编程在其中起着重要作用,如何从面向对象编程跳槽到函数式编程?你是否觉得函数式各种概念难于理解?本场 Chat 将为你解答.我将为你分享 ...

最新文章

  1. IBM与思科在融合型基础设施领域实现另一突破
  2. jQuery图片播放插件prettyPhoto使用介绍
  3. 原来颓废也是需要力气的
  4. linux find xargs grep查找给定路径下目标字符串所在文件
  5. [GAE教程]初识 Google App Engine
  6. SQL Server 2008 高可用性视频(四)-- 故障转移群集
  7. python练手经典100例微盘_20个Python练手经典案例,能全做对的人确实很少!
  8. IE浏览器网页无法缩放怎么办 解决IE浏览器网页无法缩放的方法
  9. android ripple 大小,Android L限制Ripple水波纹范围大小
  10. 【234期门诊集锦】全面了解 VMware View 5 虚拟桌面
  11. rfid 物流业务信息系统介绍
  12. win7共享wifi之bat文件
  13. pca图解读_主成分分析pca图解读,主成分分析散点图解读
  14. 西门子低代码项目团队协作方法
  15. 登陆页面✧樱花3D翻转
  16. c语言化验诊断题目,医学检验技师模拟考试题(含答案)
  17. sortWith与sortBy
  18. 一文读懂什么是进程、线程、协程
  19. javaweb07 jquery+ajax技术实现异步请求登录
  20. 淡然qqyw图标点亮系统代码审计(sql注入)

热门文章

  1. 无所不答的“自动聊天AI”
  2. 无监督学习:大数据带我们洞察现在,但小数据将带我们抵达未来
  3. 对时间序列分类的LSTM全卷积网络的见解
  4. 拒绝枯燥,趣味学python!python基础练习:趣味百题!
  5. SAP WM 自动创建TO单的JOB运行报错 - Enter the storage unit type - 对策
  6. 一些常见的名词解释(持续更新中)
  7. 当思想与机器融合:脑机接口与人类的现在、困境与未来
  8. 城市生态的机器人革命
  9. 边缘计算:5G 时代的万亿市场
  10. DeepMind提出强化学习新算法,教智能体从零学控制