千家信息网

Scala笔记整理(五):函数式编程

发表于:2025-01-31 作者:千家信息网编辑
千家信息网最后更新 2025年01月31日,[TOC]作为值传递的函数测试代码如下:package cn.xpleaf.bigdata.p4.function/** * scala中关于函数的操作 */object _01FunctionO
千家信息网最后更新 2025年01月31日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, xpleafgood bye, xpleaf

参数(类型)推断

测试代码如下:

package cn.xpleaf.bigdata.p4.functionimport 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.10.20.300000000000000040.40.50.60000000000000010.70000000000000010.80.9
  • 打印三角形
scala> (1 to 9).map("*" * _).foreach(println(_))*********************************************

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

常见高阶函数--filter函数

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

常见高阶函数--reduce函数

scala> (1 to 9).reduce((v1:Int, v2:Int) => v1 + v2)res4: Int = 45scala> (1 to 9).reduce(_ + _)res6: Int = 45scala> (1 to 9).reduceLeft(_ + _)res7: Int = 45scala> (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 = 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=2v1=3, v2=3v1=6, v2=4v1=10, v2=5v1=15, v2=6v1=21, v2=7v1=28, v2=8v1=36, v2=9res0: Int = 45
  • reductLeft的执行流程
scala> (1 to 9).reduceLeft((v1:Int, v2:Int) => pro(v1, v2))v1=1, v2=2v1=3, v2=3v1=6, v2=4v1=10, v2=5v1=15, v2=6v1=21, v2=7v1=28, v2=8v1=36, v2=9res2: Int = 45
  • reductRight的执行流程
scala> (1 to 9).reduceRight((v1:Int, v2:Int) => pro(v1, v2))v1=8, v2=9v1=7, v2=17v1=6, v2=24v1=5, v2=30v1=4, v2=35v1=3, v2=39v1=2, v2=42v1=1, v2=44res3: Int = 45

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

常见高阶函数--sortWith函数

scala> (1 to 9).sortWith((v1:Int, v2:Int) => v1 > v2).foreach(println(_))987654321scala> (1 to 9).sortWith(_ > _).foreach(println)987654321

闭包

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

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)=>factor *x函数的函数体内被引用。该函数被存入triple.然后参数变量factor从运行时的栈上被弹出。

2)mulBy再次被调用,这次factor被设为0.5.该变量在(x:Double)=>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))}
0