Scala笔记整理:函数式编程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scala笔记整理:函数式编程相关的知识,希望对你有一定的参考价值。

[TOC]


作为值传递的函数

测试代码如下:

package cn.xpleaf.bigdata.p4.function

/**
  * scala中关于函数的操作
  */
object _01FunctionOps {
  def main(args: Array[String]): Unit = {
    functionOps1
  }

  /**
    * 作为值传递的函数
    * 将一个函数作为值传递给另外一个函数变量的时候,约定需要在函数后面加上:空格和下划线
    * 相当于数据库中的别名,或者数据库表对应的视图
    */
  def functionOps1: Unit = {
    def sayHi(name:String) = println("Hello, " + name)

    def sayHello = sayHi _

    sayHello("xpleaf")
  }
}

输出结果如下:

Hello, xpleaf

匿名函数

测试代码如下:

package cn.xpleaf.bigdata.p4.function

/**
  * scala中关于函数的操作
  */
object _01FunctionOps {
  def main(args: Array[String]): Unit = {
    functionOps2
  }

  /**
    * 匿名函数,说白了就是没有函数名字
    *     匿名函数就和java中的匿名内部类一样,是只适合使用一次的函数
    *     一般如果一个函数的参数是一个函数,这种情况下多用匿名函数来作为参数进行传递
    */
  def functionOps2: Unit = {
    val printName = (name:String) => println("你好," + name)
    printName("xpleaf")
  }

}

输出结果如下:

你好,xpleaf

其实前面在学习ArrayBuffer的时候已经有使用过匿名函数:

scala> val ab = ArrayBuffer[Int](3, 8, 2, 20, 5, 7)
ab: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(3, 8, 2, 20, 5, 7)
scala> ab.sortWith((v1, v2) => v1 > v2)
res209: scala.collection.mutable.ArrayBuffer[Int] = ArrayBuffer(20, 8, 7, 5, 3, 2)

带函数参数的函数(高阶函数)

说明的作用有两个(匿名函数作为参数和返回值为匿名函数),具体参考下面的测试代码:

package cn.xpleaf.bigdata.p4.function

/**
  * scala中关于函数的操作
  */
object _01FunctionOps {
  def main(args: Array[String]): Unit = {
//    functionOps1
//    functionOps2
    functionOps3
  }

  /**
    * scala的高阶函数,就是函数的 [参数是函数] 的函数,把这种函数称为高阶函数
    */
  def functionOps3: Unit = {

    // 1.匿名函数作为参数
    def highOrderFunc(name:String, func:(String) => Unit): Unit = {
      func(name)
    }

    highOrderFunc("xpleaf", (name:String) => println("Hello, " + name))

    // 2.将匿名函数作为返回值传递给另外一个函数
    def getGoodBayFunction(gMsg: String) = (gName: String) => println(gMsg + ", " + gName)

    val goodbayFunction = getGoodBayFunction("good bye")
    goodbayFunction("xpleaf")

  }
}

输出结果如下:

Hello, xpleaf
good bye, xpleaf

参数(类型)推断

测试代码如下:

package cn.xpleaf.bigdata.p4.function

import scala.collection.mutable.ArrayBuffer

/**
  * scala中关于函数的操作
  */
object _01FunctionOps {
  def main(args: Array[String]): Unit = {
//    functionOps1
//    functionOps2
//    functionOps3
    functionOps4
  }

  /**
    * 对于匿名函数的省略写法
    */
  def functionOps4: Unit = {
    val ab = ArrayBuffer[Int](1, 2, 3, 4, 5)

    // val newAB = ab.map((x:Int) => x * 100)
    // val newAB = ab.map((x) => x * 100)
    // val newAB = ab.map(x => x * 100)
    val newAB = ab.map(100 * _)
    println(newAB)  // ArrayBuffer(100, 200, 300, 400, 500)

  }
}

输出结果如下:

ArrayBuffer(100, 200, 300, 400, 500)

常见高阶函数——map函数

遍历集合中的每一个元素,返回也是一个集合,集合大小和之前集合相等。
  • 快速产生0.1, 0.2, 0.3等方式的数字
scala> (1 to 9).map(0.1 * _).foreach(println(_))
0.1
0.2
0.30000000000000004
0.4
0.5
0.6000000000000001
0.7000000000000001
0.8
0.9
  • 打印三角形
scala> (1 to 9).map("*" * _).foreach(println(_))
*
**
***
****
*****
******
*******
********
*********

在这里,我们还用到了foreach,它和map很像,只不过它的函数并不返回任何值,foreach只是简单的将函数应用到每个元素而已。

常见高阶函数——filter函数

按照过滤条件,将原集合中不符合条件的数据过滤掉
  • 输出所有匹配某个特定条件的元素,得到一个序列中的所有偶数
scala> (1 to 9).filter(line => line % 2 == 0).foreach(println(_))
2
4
6
8

scala> (1 to 9).filter(_ % 2 ==0).foreach(println)
2
4
6
8

常见高阶函数——reduce函数

scala> (1 to 9).reduce((v1:Int, v2:Int) => v1 + v2)
res4: Int = 45
scala> (1 to 9).reduce(_ + _)
res6: Int = 45
scala> (1 to 9).reduceLeft(_ + _)
res7: Int = 45
scala> (1 to 9).reduceRight(_ + _)
res8: Int = 45

可以写下面一个函数来验证reduce函数的执行过程:

scala> val process = (v1:Int, v2:Int) => println(s"v1=${v1}, v2=${v2}")
process: (Int, Int) => Unit = <function2>
scala> def pro(v1:Int, v2:Int):Int = {
     |   println(s"v1=${v1}, v2=${v2}")
     |   v1 + v2
     | }
pro: (v1: Int, v2: Int)Int
  • reduce的执行流程
scala> (1 to 9).reduce((v1:Int, v2:Int) => pro(v1, v2))
v1=1, v2=2
v1=3, v2=3
v1=6, v2=4
v1=10, v2=5
v1=15, v2=6
v1=21, v2=7
v1=28, v2=8
v1=36, v2=9
res0: Int = 45
  • reductLeft的执行流程
scala> (1 to 9).reduceLeft((v1:Int, v2:Int) => pro(v1, v2))
v1=1, v2=2
v1=3, v2=3
v1=6, v2=4
v1=10, v2=5
v1=15, v2=6
v1=21, v2=7
v1=28, v2=8
v1=36, v2=9
res2: Int = 45
  • reductRight的执行流程
scala> (1 to 9).reduceRight((v1:Int, v2:Int) => pro(v1, v2))
v1=8, v2=9
v1=7, v2=17
v1=6, v2=24
v1=5, v2=30
v1=4, v2=35
v1=3, v2=39
v1=2, v2=42
v1=1, v2=44
res3: Int = 45

这样的话执行过程就非常清晰了,reduce和reduceLeft都是从左边的操作数开始,而reduceRight是从右边的操作数开始。

常见高阶函数——sortWith函数

scala> (1 to 9).sortWith((v1:Int, v2:Int) => v1 > v2).foreach(println(_))
9
8
7
6
5
4
3
2
1

scala> (1 to 9).sortWith(_ > _).foreach(println)
9
8
7
6
5
4
3
2
1

闭包

函数的变量不在其作用域内被调用,就是闭包的概念,看下面一个例子:

def closePackage: Unit ={
    def mulBy(factor:Double) = (x:Double) => factor * x
    val triple = mulBy(3)
    val half = mulBy(0.5)
    println(triple(14) +" " + half(14)) //42, 7
}

1)mulBy的首次调用将参数变量factor设为3,。该变量在(x:Double)=&gt;factor *x函数的函数体内被引用。该函数被存入triple.然后参数变量factor从运行时的栈上被弹出。

2)mulBy再次被调用,这次factor被设为0.5.该变量在(x:Double)=&gt;factor *x函数的函数体内被引用,该函数被存入half.

每一个函数被称为闭包(closure)。闭包有代码和代码用到的任何非局部变量定义构成。

其实上面的mulBy函数就类似于下面这个:

def mulBy1(factor:Double, x:Double) = {
    factor * x
}

原本还希望进一步写成下面这样子的:

def mulBy2(factor:Double, x:Double) = {
    def work(x:Double) = {
        factor * x
    }
    return work
}

但在scala中不支持这样的写法,下面这样写才可以:

def mulBy2(factor:Double) = {
    (x:Double) => factor * x
}
// 加个return也不行

之前在Python中使用过闭包,类似上面的例子,Python就可以这样写:

def mulBy(factor):
    def work(x):
        return factor * x
    return work

测试如下:

>>> def mulBy(factor):
...     def work(x):
...             return factor * x
...     return work
...
>>> triple = mulBy(3)
>>> half = mulBy(0.5)
>>> triple(14)
42
>>> half(14)
7.0

当然,只是Python的语法格式就没有scala那么灵活了。

柯里化(currying函数)

1、柯里化(currying)指的是将原来接受2个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数作为参数的函数。

2、在函数调用的过程中,就变为了两个函数连续调用的形式。在Spark源码中,也有体现,所以对()()这种形式的Curring函数,一定要掌握。

以下函数接受一个参数,生成另一个接受单个参数的函数,要计算两个数的乘积,调用如下:

/**
    * curryingFunction: 柯里化函数
    */
def curryingFunction ={
    def totalSum(x:Int, y:Int) = println( x + y)

    //totalSum(4,5)

    def totalSumAtTime(x:Int) = (y:Int) => x + y
    println(totalSumAtTime(4)(5))
}

以上是关于Scala笔记整理:函数式编程的主要内容,如果未能解决你的问题,请参考以下文章

大数据笔记(二十五)——Scala函数式编程

scala学习笔记-函数式编程(14)

Scala笔记--函数式编程

理解Scala的函数式编程思想

Scala函数式编程函数式的数据结构 下

scala入门笔记