zoukankan      html  css  js  c++  java
  • 函数式语言的特性

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

    • 高阶函数(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

  • 相关阅读:
    Vue-cli / webpack 加载静态js文件的方法
    shell curl 下载图片并另存为(重命名)
    sublime 技巧与快捷键篇
    es5 温故而知新 创建私有成员、私有变量、特权变量的方法
    es5 温故而知新 简单继承示例
    js 万恶之源 是否滚动到底部?
    ES6 基础知识
    jquery操作select(取值,设置选中)
    WebApi深入学习--特性路由
    Asp.net 代码设置兼容性视图
  • 原文地址:https://www.cnblogs.com/feng9exe/p/8649836.html
Copyright © 2011-2022 走看看