教材:快学Scala
chapter 12. 高阶函数 Higher-Order Functions
12.1 functions as values
- 方法(method)和函数(function)是不一样的,method不是值,function是值
- method用def定义 function可以赋值给一个val
-
方法转换成函数:
val func = 方法 _
Scala无法直接操作(manipulate)方法,只能直接操作函数 - 函数的用法:1.调用 2.传递(到变量/到另一个函数)
12.2 匿名函数
(x: Double) => 3 * x
scala> val triple = (x: Double) => 3*x // 定义函数
triple: Double => Double = <function1>
scala> def triple(x: Double) = 3*x // 定义方法
triple: (x: Double)Double
- 函数参数可以不用圆括号
()
用花括号{}
括住
e.g.
Array(3.14, 1.42, 2.0).map{ (x: Double) => 3 * x }
其中
(x: Double) => 3 * x
是函数map
的参数!!
12.3 函数作为参数
- 作为参数的函数,它的类型写作
(参数类型) => 结果类型
- 带函数参数的函数叫做高阶函数(higher-order function)
12.4 参数类型推断 Parameter Inference
- 在必要信息给出的前提下Scala可自动推断参数属于什么类型
def quarter(f: (Double) => Double) = f(0.25) // 类型为((Double) => Double) => Double
quarter((x: Double) => 3 * x)
quarter((x) => 3 * x) // 从传入参数类型为(Double) => Double) 知道x的类型为Double 可省略
quarter(x => 3 * x) // 只有一个参数(x) 可写成x
quarter(3 * _) // 如果x在=>右侧只出现一次,可以用_代替x
12.5 一些有用的高阶函数
- 通用原则:如果你要的是一个序列的值,那么想办法从一个简单的序列转化得出
(1 to 9).map("*" * _).foreach(println _)
(1 to 9).filter(_ % 2 == 0)
// 2,4,6,8
(1 to 9).reduceLeft(_ * _)
// 从左到右reduce (..((1 * 2) * 3) * ... * 9)
(1 to 9).foldLeft(100)(_ + _)
// 145 与reduceLeft区别是提供了迭代初始值
"Mary has a little lamb".split(" ").sortWith(_.length < _.length)
等价于
"Mary has a little lamb".split(" ").sortWith((a: String, b: String) => a.length < b.length)
12.6 闭包 Closures
- A closure consists of code together with the definitions of any nonlocal variables that the code uses.
- Java 8支持一种形式上受限的闭包
12.7 SAM转换
- 背景:Java不支持函数参数,需要将动作放进一个实现某接口的类中,再传递类的实例作为参数
- 这种接口通常只有单个抽象方法(Single Abstract Method) Java中被称为SAM类型
- 问题:如何实现函数参数 -> SAM参数的转换?
- 答案:隐式转换(implicit)语法,通过自定义隐式转换实现需求
implicit def makeAction(action: (ActionEvent) => Unit) = // action: 函数参数
new ActionListener { // ActionListener: 需要转换成的实例
override def actionPerformed(event: ActionEvent) { action(event) } // actionPerformed: SAM
}
// 效果:在所有预期ActionListener对象的地方传入任何(ActionEvent)=>Unit函数
12.8 柯里化 Currying
def mulOneAtATime(x: Int)(y: Int) = x * y
- 用途:把多参数中的某个参数单独拎出来,提供用于类型推断的信息
val a = Array("hello", "world")
val b = Array("Hello", "World")
a.corresponds(b)(_.equalsIgnoreCase(_))
corresponds的定义为
def corresponds[B](that: Seq[B])(p: (A, B) => Boolean): Boolean
通过that参数知道B类型,用来推断函数p的输入类型
12.9 控制抽象 Control Abstractions
- 函数是"头等公民":一系列语句 -> 不带参数也没有返回值的函数
- "不带参数也没有返回值的函数" 的函数类型:
() => Unit
def runInThread(block: () => Unit) { // 输入是一个过程(控制结构),表现为一个"不带参数也没有返回值的函数"block
new Thread {
override def run() { block() }
}.start()
}
// 调用:{}内的是runInThread的参数,一个无参数无返回的函数
runInThread { () => println("Hi"); Thread.sleep(1000); println("Bye") }
想在调用中省略()=>
写法,使用call-by-name(换名调用)
def runInThread(block: => Unit) { // call-by-name语法:参数名:=>类型
new Thread {
override def run() { block } // block不加()
}.start()
}
// 调用:不用加()=>
runInThread { println("Hi"); Thread.sleep(1000); println("Bye") }
- call-by-name参数:函数被调用的时候,参数表达式不会被求值,而在函数体内每次出现都会求值一次
- call-by-value参数:就是普通的参数,函数被调用的时候会被求值,然后函数体内每次出现都不会重新计算
def until(condition: => Boolean)(block: => Unit) {
if (!condition) {
block
until(condition)(block)
}
}
var x = 10
until (x == 0) {
x -= 1
println(x)
}
12.10 return表达式
-
return
在Scala的作用:函数A的函数体内定义了匿名函数B,若B的函数体内使用return语句,则直接跳出A
def indexOf(str: String, ch: Char): Int = {
var i = 0
until (i == str.length) {
if (str(i) == ch) return i // 直接跳出indexOf
i += 1
}
return -1
}
练习答案
def values(fun: (Int) => Int, low: Int, high: Int) = (low to high) zip ((low to high).map(fun))
reduceLeft(max(_,_))
fibo(x: Int) = if(x != 0) (1 to x).reduceLeft(_*_) else 1
fibo(x: Int) = (1 to x).foldLeft(1)(_*_)
def largest(fun:(Int)=>Int, inputs:Seq[Int]) = inputs.map(fun).reduceLeft(max(_,_))
def largestAt(fun:(Int)=>Int, inputs:Seq[Int]) = inputs.zip(inputs.map(fun)).reduceLeft((a, b) => if (a._2 > b._2) a else b )._1
def adjustToPair[A, B, C](fun: (A, B) => C) = (pair: (A, B)) => fun(pair._1, pair._2)
(1 to 10).zip((11 to 20)).map(adjustToPair[Int, Int, Int](_+_))
Array("i", "am", "cat").corresponds(Array(1, 2, 3))(_.length == _)
def myCorresponds[A, B](iThis: Seq[A], iThat: Seq[B], p: (A, B) => Boolean): Boolean = {
val pairs = iThis zip iThat
// pairs.map(adjustToPair[A, B, Boolean](p)).filter(_ == false).length == 0
// 看完13.8以后学到的新姿势
pairs.map(adjustToPair[A, B, Boolean](p)).forall(_ == true)
}
def unless(condition: => Boolean)(block: => Unit) = if (!(condition)) { block }
网友评论