泛函编程(2)-初次体验泛函编程

  泛函编程和数学方程式解题相似;用某种方式找出问题的答案。泛函编程通用的方式包括了模式匹配(pattern matching)以及递归思维(Recursive thinking)。我们先体验一下:(在阅读本系列博客文章之前,相信读者已经对Scala语言及REPL用法有所了解了。在这就不去解释Scala的语法语意了。)

先来个简单的:

1 def reportError(msgId: Int): String = msgId match {
2      | case 1 => "Error number 1."
3      | case 2 => "Error number 2."
4      | case 3 => "Error number 3."
5      | case _ => "Unknown error!"
6      | }
7 reportError: (msgId: Int)String

很明显,这个函数的是一个纯函数,也是一个完整函数。因为函数主体涵盖了所有输入值(注意: case _ =>)。我们可以预知任何输入msgId值所产生的结果。还有,函数中没有使用任何中间变量。看看引用情况:

1 reportError(2)
2 res3: String = Error number 2.
3
4 scala> reportError(-1)
5 res4: String = Unknown error!

恰如我们预测的结果。

再来看看一个递归(Recursion)例子:阶乘(Factorial)是一个经典样例:

1 def factorial(n: Int): Int = {
2       if ( n == 1) n
3       else n * factorial(n-1)
4   }                                               //> factorial: (n: Int)Int
5   factorial(4)                                    //> res48: Int = 24

也可以用模式匹配方式:

1 def factorial_1(n: Int): Int = n match {
2       case 1 => 1
3       case k => k * factorial(n-1)
4   }                                               //> factorial_1: (n: Int)Int
5   factorial_1(4)                                  //> res49: Int = 24

用模式匹配方式使函数意思表达更简洁、明了。

我们试着用“等量替换”方式逐步进行约化(reduce)

1 factorial(4)
2   4 * factorial(3)
3   4 * (3 * factorial(2))
4   4 * (3 * (2 * factorial(1)))
5   4 * (3 * (2 * 1)) = 24

可以得出预料的答案。

递归程序可以用 loop来实现。主要目的是防止堆栈溢出(stack overflow)。不过这并不妨碍我们用递归思维去解决问题。 阶乘用while loop来写:

1 def factorial_2(n: Int): Int = {
2          var k: Int = n
3          var acc: Int = 1
4          while (k > 1) { acc = acc * k; k = k -1}
5          acc
6   }                                               //> factorial_2: (n: Int)Int
7   factorial_2(4)                                  //> res50: Int = 24

注意factorial_2使用了本地变量k,acc。虽然从表达形式上失去了泛函编程的优雅,但除了可以解决堆栈溢出问题外,运行效率也比递归方式优化。但这并不意味着完全违背了“不可改变性”(Immutability)。因为变量是锁定在函数内部的。

最后,也可用tail recursion方式编写阶乘。让编译器(compiler)把程序优化成改变成 loop 款式:

1 def factorial_3(n: Int): Int = {
2     @annotation.tailrec
3       def go(n: Int, acc: Int): Int = n match {
4           case 1 => acc
5           case k => go(n-1,acc * k)
6       }
7       go(n,1)
8   }                                               //> factorial_3: (n: Int)Int
9   factorial_3(4)                                  //> res51: Int = 24

得出的同样是正确的答案。这段程序中使用了@annotation.tailrec。如果被标准的函数不符合tail recusion的要求,compiler会提示。

时间: 2024-10-06 12:34:39

泛函编程(2)-初次体验泛函编程的相关文章

Java编程那些事儿9——网络编程基础

对于初学者,或者没有接触过网络编程的程序员,会觉得网络编程涉及的知识很高深,很难,其实这是一种误解,当你的语法熟悉以后,其实基本的网络编程现在已经被实现的异常简单了. 1.4.1 网络编程是什么? 网络编程的本质是两个设备之间的数据交换,当然,在计算机网络中,设备主要指计算机.数据传递本身没有多大的难度,不就是把一个设备中的数据发送给两外一个设备,然后接受另外一个设备反馈的数据. 现在的网络编程基本上都是基于请求/响应方式的,也就是一个设备发送请求数据给另外一个,然后接收另一个设备的反馈. 在网

Java编程那些事儿105——网络编程技术4

13.2.4 UDP编程 网络通讯的方式除了TCP方式以外,还有一种实现的方式就是UDP方式.UDP(User Datagram Protocol),中文意思是用户数据报协议,方式类似于发短信息,是一种物美价廉的通讯方式,使用该种方式无需建立专用的虚拟连接,由于无需建立专用的连接,所以对于服务器的压力要比TCP小很多,所以也是一种常见的网络编程方式.但是使用该种方式最大的不足是传输不可靠,当然也不是说经常丢失,就像大家发短信息一样,理论上存在收不到的可能,这种可能性可能是1%,反正比较小,但是由

Windows 2016 TP5上的Docker初次体验

本文讲的是Windows 2016 TP5上的Docker初次体验,[编者的话]微软5.28发布Windows 2016 Technical Preview 5,作者第一时间上手,记录发现的新变化,看样子要接着往下写呢.这是第一篇,快来瞅瞅吧. 昨天(2016年4月28日),微软宣布Windows 2016 Technical Preview 5可用.我当然要赶紧查看一下新的TP5和去年11月份发布的TP4有什么不同了. 因为还没找到Azure模板(更新:今天我找到了Windows Server

iOS开发那些事-iOS网络编程同步GET方法请求编程

iOS SDK为HTTP请求提供了同步和异步请求两种不同的API,而且可以使用GET或POST等请求方法.我们先了解其中最为简单的同步GET方法请求. 为了学习这些API的使用MyNotes"备忘录"应用实例,数据来源于服务器端,而不是本地的Notes.xml(或Notes.json)文件. 首先实现查询业务,查询业务请求可以在主视图控制器MasterViewController类中实现,其中MasterViewController.h代码如下: #import <UIKit/U

树-一道编程题,用c++编程,求助

问题描述 一道编程题,用c++编程,求助 给定一颗无根树,假设它有n个节点,节点编号从1到n,求任意两点之间的距离之和,也就是求任意一点到其它点的距离之和,边长都为1.要求时间复杂度为O(n) 解决方案 先做一遍DFS求出所有节点到根节点的距离之和,然后可以发现,如果知道到一个点的距离之和,可以用O(1)求出所有节点到它相邻点的距离之和 解决方案二: /* ***********************************************Author :xdloveCreated T

求助 编程 就业 能力-编程到底难不难?编程这一行业一定要智力很高么?

问题描述 编程到底难不难?编程这一行业一定要智力很高么? 我是一个"貌似"很热爱编程的人,我想把这个编程作为我的职业.我现在学的专业是临床医学,我觉得这个很烂,没兴趣,很抵触.我唯一能走的可能就是软件.但是,我不知道我是不是真正的能在这个领域做出什么成就来,不需要很高的成绩. 或者说,我不知道我到底适不适合干编程这行?这行业,将来会面临什么样的问题? 所以,我问问,这行业最难得是什么,不仅仅是知识方面,还有将来就业方面?或者,怎么才能知道我是不是叶公好龙,是不是真真适合这个行业?有没有

初次体验今日头条广告 收益堪比联盟广告

中介交易 http://www.aliyun.com/zixun/aggregation/6858.html">SEO诊断 淘宝客 云主机 技术大厅 笔者因为之前有幸被邀请开通了今日头条广告系统,可以投放自己的广告,也就是今日头条的自营广告系统.不过,还有一些幸运的自媒体人士还可以投放今日头条的广告.这样就是说,今日头条这个平台投放的这个是什么广告,你的文章下面也会显示这个广告,就跟联盟广告一样!根据用户的浏览记录,自动推荐适合用户的广告. 初次体验头条自带广告系统 一开始我就用了头条自营

VB编程 及EXCEL 的VBA编程,用什么把一段代码括起来啊(就是用什么东西来实现C语言中的{}功能啊)?

问题描述 VB编程及EXCEL的VBA编程,用什么把一段代码括起来啊(就是用什么东西来实现C语言中的{}功能啊)? 解决方案 解决方案二:不是有begin和end吗解决方案三:region?C的{}有很多啊,只能你VB书都没看过if...endif-------------if{}for...endfor---------for{}解决方案四:for..next.............我草解决方案五:学c的时候用按键精灵的时候我也愣了一阵子...很多是用end,if之后用endif,while

c语言-请教一个C编程 打印输出图像的算法编程

问题描述 请教一个C编程 打印输出图像的算法编程 解决方案 大概就是这样,建立笛卡尔坐标系. 用point()函数里的嵌套for循环来输出每一个字符,然后把代表坐标的i和j传递给getChar()函数通过坐标来决定输出的是什么字符. 解决方案二: char getChar(int x,int y,int n) { if(x<0) x=-x; if(y<0) y=-y; if(x>y) { if(n-x<=2) return 'x'+n-x; else return '0'+n-x-