函数式语言的特性

函数式语言当然还少不了以下特性:

  • 高阶函数(Higher-order function)
  • 偏应用函数(Partially Applied Functions)
  • 柯里化(Currying)
  • 闭包(Closure)

 

高阶函数就是参数为函数或返回值为函数的函数。有了高阶函数,就可以将复用的粒度降低到函数级别,相对于面向对象语言,复用的粒度更低。

举例来说,假设有如下的三个函数,

def sumInts(a: Int, b: Int): Int = if (a > b) 0 else a + sumInts(a + 1, b) def sumCubes(a: Int, b: Int): Int = if (a > b) 0 else cube(a) + sumCubes(a + 1, b) def sumFactorials(a: Int, b: Int): Int = if (a > b) 0 else fact(a) + sumFactorials(a + 1, b) 

分别是求a到b之间整数之和,求a到b之间整数的立方和,求a到b之间整数的阶乘和。

其实这三个函数都是以下公式的特殊情况
\sum_{n=a}^{b}{f(n)}
三个函数不同的只是其中的f不同,那么是否可以抽象出一个共同的模式呢?

我们可以定义一个高阶函数sum:

def sum(f: Int => Int, a: Int, b: Int): Int = if (a > b) 0 else f(a) + sum(f, a + 1, b) 

其中参数f是一个函数,在函数中调用f函数进行计算,并进行求和。

然后就可以写如下的函数

def sumInts(a: Int, b: Int) = sum(id, a, b) def sumCubs(a: Int, b: Int) = sum(cube, a, b) def sumFactorials(a: Int, b: Int) = sum(fact, a, b) def id(x: Int): Int = x def cube(x: Int): Int = x * x * x def fact(x: Int): Int = if (x == 0) 1 else fact(x - 1) 

这样就可以重用sum函数来实现三个函数中的求和逻辑。
(示例来源:

高阶函数提供了一种函数级别上的依赖注入(或反转控制)机制,在上面的例子里,sum函数的逻辑依赖于注入进来的函数的逻辑。很多GoF设计模式都可以用高阶函数来实现,如Visitor,Strategy,Decorator等。比如Visitor模式就可以用集合类的map()或foreach()高阶函数来替代。

函数式语言通常提供非常强大的集合类(Collection),提供很多高阶函数,因此使用非常方便。

比如说,我们想对一个列表中的每个整数乘2,在命令式编程中需要通过循环,然后对每一个元素乘2,但是在函数式编程中,我们不需要使用循环,只需要使用如下代码:

scala> val numbers = List(1, 2, 3, 4) numbers: List[Int] = List(1, 2, 3, 4) scala> numbers.map(x=>x*2) res3: List[Int] = List(2, 4, 6, 8) 

(示例来源:Programming Scala: Tackle Multi-Core Complexity on the Java Virtual Machine一书的Introduction)

其中x=>x*2是一个匿名函数,接收一个参数x,输出x*2。这里也可以看出来函数式编程关注做什么(x*2),而不关注怎么做(使用循环控制结构)。程序员完全不关心,列表中的元素是从前到后依次计算的,还是从后到前依次计算的,是顺序计算的,还是并行进行的计算,如Scala的并行集合(Parallel collection)。

使用集合类的方法,可以使对一些处理更简单,例如上面提到的求阶乘的函数,如果使用集合类,就可以写成:

def fact(n: Int): Int = (1 to n).reduceLeft((acc,k)=>acc*k) 

其中(1 to n)生成一个整数序列,而reduceLeft()高阶函数通过调用匿名函数将序列化简。

那么,在大数据处理框架Spark中,一个RDD就是一个集合。以词频统计的为例代码如下:

val file = spark.textFile("hdfs://...") val counts = file.flatMap(line => line.split(" ")) .map(word => (word, 1)) .reduceByKey(_ + _) counts.saveAsTextFile("hdfs://...") 

(示例来源:

示例里的flatMap(),map(),和集合类中的同名方法是一致的,这里的map方法的参数也是一个匿名函数,将单词变成一个元组。写这个函数的人不用关心函数是怎么调度的,而实际上,Spark框架会在多台计算机组成的分布式集群上完成这个计算。

此外,如果对比一下Hadoop的词频统计实现:WordCount - Hadoop Wiki ,就可以看出函数式编程的一些优势。

函数式编程语言还提供惰性求值(Lazy evaluation,也称作call-by-need),是在将表达式赋值给变量(或称作绑定)时并不计算表达式的值,而在变量第一次被使用时才进行计算。这样就可以通过避免不必要的求值提升性能。在Scala里,通过lazy val来指定一个变量是惰性求值的,如下面的示例所示:

 

https://www.zhihu.com/question/28292740

转载于:https://www.cnblogs.com/feng9exe/p/8649836.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值