这篇是Scala讲座第七篇的第三部分,总括了函数式编程的思考方法。
在进行什么编程的时候,你用什么方法来思考呢?用命令是方法来考虑的话,一定是考虑“首先有一个变量,然后一边循环一边判断一下这样的条件,接着进行这种操作···”这样的操作步骤吧。
面向对象式方法来考虑时,一定是先考虑“程序中出现的这个对象里有什么东西(数据)呀?”,然后再给对象分配 “动作”(方法)吧。整体印象应该是,“对象”们互相分配好自己的工作,对象自己只做自己的工作其他的工作交给其他对象来完成,“对象”们互相合作来完成一个处理吧。即使是面向对象的情况下,对于一般语言来说,对象方法内也是用命令方式来实现的。
那么,函数式编程该是怎样的景象呢?函数式编程是“将函数应用在值或者对象上”这种思考方法,也就是函数只是将值或者对象转换成不同的别的东西。这样说,可能函数式方法还是很难在读者脑中浮现,那么就以“命令型”和“函数式”两种类型的例子为基础进行阐述吧。
[Scala讲座]题目:架子上的苹果
题目内容为模拟“架子上有100个苹果,一个个剥皮吃掉***一个也没有了。“这个过程。运行结果的样子因该如下所示:
当苹果只有1或0个的时候,由于不是复数情况,所以处理方法有所不同。先用命令方式来实现一下。
- > scala AppleCounter
- 99 apples on the wall.
- 98 apples on the wall.
- :
- 2 apples on the wall.
- 1 apple on the wall.
- no apple on the wall.
首先准备好列表变量appleList,接着做100次循环。循环中用变量counter来存放当前所剩的苹果数,并根据该数字向例表末尾加入描述文本,其中对于1个和0个的情况进行特殊处理。***使用appleList的foreach方法对每一列表成员进行打印操作。题外话,由于Scala是函数式+面向对象语言,所以能够以这种命令式方法来编程,这对于不熟悉函数式编程的用户来说也还真不错。
- object AppleCounter{
- def main(args:Array[String]):Unit = {
- var appleList:List[String] = List()
- for(i <- 1 to 100) {
- var counter = 100 -i
- if (counter == 1) appleList = appleList ::: List("1 apple on the wall.")
- if (counter == 0) appleList = appleList ::: List("no apple on the wall.")
- if (counter != 1 && counter != 0)
- appleList = appleList ::: List(counter + " apples on the wall.")
- }
- appleList.foreach(x => println(x))
- }
- }
好了,这次用函数式的方法来重新实现一下同样的逻辑。那么该怎样考虑函数式的实现呢?下面是实现例子。
这里不是想说明“程序短了不少啊···”,而是希望大家明白思考方法的不同之处(不过这里并没有声明变量appleList的类型,能够进行如此复杂的类型推断也真是挺厉害的呀!)。编写这段程序的时候,我一开始就没有考虑到循环这个概念。比起循环,我考虑的是如何将函数用在数字列表变量上。首先不是逻辑,而是创建用于执行函数的对象(这里是数字列表),然后考虑选择哪种函数来执行。
- object AppleCounter {
- def main(args:Array[String]):Unit = {val appleList = (0 until 100).reverse.map(x => x match {
- case 1 => "1 apple on the wall."
- case 0 => "no apple on the wall."
- case _ => x + " apples on the wall."
- })
- appleList.foreach(x => println(x))
- }
- }
首先考虑创建如下的列表对象。
这个通过(0 until 100).reverse部分来实现。接着对于这个列表的一个个数字,考虑返回数字相对应字符串的函数。对应部分如下
- List(99, 98, 97, .... 2, 1, 0)
用Scala进行像样的函数式编程时,map是最重要的函数之一。也就是,对于列表中的每个元素用map函数传进来的函数执行一下,然后返回他的结果列表。这里传递给map函数的参数(函数对象)内容是:对于1返回1 apple on the wall.;对于0返回no apple on the wall;其他情况比如22,返回22 apples on the wall。根据该内容,map函数返回如下列表
- (上面的列表).map(x => x match {
- case 1 => "1 apple on the wall."
- case 0 => "no apple on the wall."
- case _ => x + " apples on the wall."
- })
- List("99 apples on the wall.", "98 apples on the wall.", …"no apple on the wall.")
***一行类似于命令式编程,对于例表appleList用foreach方法循环遍历,并用foreach传进的函数打印列表所有的元素,然后程序结束。
函数map不仅在List类中有,其他很多类中也有。
Scala讲座 图:map函数概念图
函数式语言中List具有强大的功能,这里再一次强调,函数式编程不是考虑如何循环,而是首先考虑创建作为操作源的列表,然后考虑将怎样的函数适用于列表中的元素。由于使用如此风格的编程方式,所以就不需要保存用于循环的计数器呀,临时变量什么的了。
Scala讲座到这里,第七篇第三部分内容就结束了。
【编辑推荐】