12 高阶函数

作为值的函数

  • 变量中存放函数
  • 将方法变为函数
import scala.math._
val num = 3.45
val fun = ceil _        //这里的 _ 表示这个函数,而不是没有传参数
//或 val fun = ceil(_)
//fun: Double => Double = <function1>
println(fun(3.45))
  • ceil是math这个包对象的方法,如果有来自类的方法,将其变成函数的方式
scala> val f = (_:String).charAt(_:Int)
f: (String, Int) => Char = <function2>

scala> val f1:(String,Int)=>Char = _.charAt(_)
f1: (String, Int) => Char = <function2>

scala> val add : (Int,Int)=>Int = (x,y)=>x+y
add: (Int, Int) => Int = <function2>
  • 可以对函数调用传递(存放在变量中或作为参数传递给另一个函数)
scala> fun(3.13)
res10: Double = 4.0

scala> Array(3.1,3.4,5.7).map(fun)
res11: Array[Double] = Array(4.0, 4.0, 6.0)

匿名函数

  • 函数不一定有名字,就像数字也不一定有名字
  • 以def定义的都是方法,不是函数
  • 结合第二章的内容
  • 函数名的后面有时候是冒号有时候是等号。如果右侧显式指明函数的类型就用冒号,如果不显式指明函数的类型就用等号,函数类型可以通过自动推断得到
scala> (x:Double) => 3*x
res12: Double => Double = <function1>
//或者加名字
scala> val triple=(x:Double) => 3*x
triple: Double => Double = <function1>

scala> val triple1:(Double)=>Double =  3*_
triple1: Double => Double = <function1>

scala> val triple1:(Double)=>Double = x => 3*x
triple1: Double => Double = <function1>
//使用map,map后面也可以跟大括号
scala> Array(3.1,3.4,5.7).map(triple1)
res13: Array[Double] = Array(9.3, 10.2, 17.1)
scala> Array(3.1,3.4,5.7).map{triple1}
res14: Array[Double] = Array(9.3, 10.2, 17.1)

带函数参数的函数

  • 函数作为参数
  • 高阶函数:函数作为参数或返回值
  • 如下示例
//函数作为参数
def valueAtQuator(f:(Double)=>Double):Double = f(0.25)
//(f: Double => Double)Double
//函数作为返回值
def mulBy(factor:Double):Double=>Double = {(x:Double)=>factor*x}
//Double => Double = <function1>

参数类型推断

  • scala会自动类型推断
  • 给出_的类型有助于将方法变为函数
//手动指明类型
scala> valueAtQuator((x:Double) => 3*x)
res19: Double = 0.75
//scala自动推断x的类型
scala> valueAtQuator((x) => 3*x)
res20: Double = 0.75
//只有一个参数的时候,略去x的括号
scala> valueAtQuator(x => 3*x)
res21: Double = 0.75
//如果x在=>右边只出现了一次,可以用_代替
scala> valueAtQuator(3*_)
res22: Double = 0.75

scala> (_:String).substring(_:Int,_:Int)
res0: (String, Int, Int) => String = <function3>

一些有用的高阶函数

  • 练习scala集合库中的一些接受函数参数的方法
  • foreach 将函数应用到每个元素,不返回值
  • filter交出所有满足条件的元素
  • reduceLeft
scala> (1 to 9).map("*"*_).foreach(println)
*
**
***
****
*****
******
*******
********
*********

scala> (1 to 9).filter( _ % 2 ==0)
res3: scala.collection.immutable.IndexedSeq[Int] = Vector(2, 4, 6, 8)

scala> (1 to 9).map(_.toString).reduceLeft( (x,y) => (s"f(${x},${y})"))
res8: String = f(f(f(f(f(f(f(f(1,2),3),4),5),6),7),8),9)

scala> (1 to 9).map(_.toString).reduceRight( (x,y) => (s"f(${x},${y})"))
res9: String = f(1,f(2,f(3,f(4,f(5,f(6,f(7,f(8,9))))))))

scala> Array(4,1,2,6).sortWith(_<_)
res11: Array[Int] = Array(1, 2, 4, 6)

闭包

  • 闭包由代码和代码用到的任何非局部变量定义构成
  • 下面的示例中,每一个函数都有自己的factor设置
def mulBy(factor:Double):Double=>Double = {(x:Double)=>factor*x}

scala> def triple = mulBy(3)
triple: Double => Double

scala> def hale = mulBy(0.5)
hale: Double => Double

SAM转换

  • Single Abstract method 带有单个抽象方法
  • scala函数到java SAM接口转换只对函数字面量有效

柯里化

  • 将原来接受两个参数的函数变成新的接受一个参数的函数的过程,新的函数返回一个以原有第二个参数作为参数的函数。
scala> val mul = (x:Int,y:Int)=>x*y
mul: (Int, Int) => Int = <function2>

scala> val mulOneATime =(x:Int)=>((y:Int)=>x*y)
mulOneATime: Int => (Int => Int) = <function1>

scala> mulOneATime(6)(7)
res12: Int = 42
//柯里化简化版 方法
scala> def mulOneATime1 (x:Int)(y:Int)=x*y
mulOneATime1: (x: Int)(y: Int)Int
//柯里化函数版
scala> val mulOneATime2 = (x:Int) => (y:Int)=>x*y
mulOneATime2: Int => (Int => Int) = <function1>
  • 柯里化把某个函数参数单独提出来,以提供更多用于类型推断的信息
  • _.equalsIgnoreCase(_)是一个经过柯里化的参数的形式传递
scala> val a = Array("hello","world")
a: Array[String] = Array(hello, world)

scala> val b = Array("Hello","world")
b: Array[String] = Array(Hello, world)

scala> a.corresponds(b)(_.equalsIgnoreCase(_))
res13: Boolean = true
//corresponds源码
  def corresponds[B](that: GenSeq[B])(p: (A,B) => Boolean): Boolean = {
    val i = this.iterator
    val j = that.iterator
    while (i.hasNext && j.hasNext)
      if (!p(i.next(), j.next()))
        return false

    !i.hasNext && !j.hasNext
  }
发布了75 篇原创文章 · 获赞 83 · 访问量 7万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章