chapter 12 高阶函数
标签:快学scala
一、笔记
- scala可以在变量里存放函数,比如:
import scala.math._
val num = 3.14
val fun = ceil _ //fun: Double => Double = <function1>
从技术上讲,_将ceil方法转成了函数,scala中,无法直接操纵方法,而只能直接操纵函数。我们可以调用它,或者传递它,存放在变量中,或者作为参数传递给另一个函数。fun(num)
2. 匿名函数。可以将函数参数包在花括号中而不是用圆括号:
Array(3.14, 1.42, 2.0).map{ (x: Double)=> 3 * x }
- 高阶函数。
map方法将一个函数应用到某个集合的所有元素并返回结果。
(1 to 9).map(0.1 * _) //如果想要一个序列的值,想办法从一个简单的序列化得出
(1 to 9).map("*" * _).foreach(println) //打印三角形
foreach的函数并不返回结果,foreach只是简单地将函数应用到每个元素而已。filter方法输出所有匹配某个特定条件的元素。
(1 to 9).filter(_ % 2 == 0) //得到所有偶数
reduceLeft方法接受一个二元的函数,一个带有两个参数的函数,并将它应用到序列中的所有元素,从左到右。
(1 to 9).reduceLeft(_ * _) //9的阶乘
- 闭包,可以在任意作用域内定义函数:在函数体内,可以访问到相应作用域内的任何变量,你的函数可以在变量不再处于作用域内时被调用。闭包由代码和代码用到的所有非局部变量构成。
def mulBy(factor: Double) =(x:Double) => factor *x
val triple = mulBy(3)
val half = mulBy(0.5)
println(triple(14) + " " + half(14)) //42,7
- 柯里化指将原来接受两个参数的函数变成新的接受一个参数的函数的过程。新的函数返回一个以原有第二个参数作为参数的函数。
def mul(x: Int, y: Int)= x*y
def mulOneAtAtime(x: Int) = (y: Int)=> x*y //生成接受一个参数的函数
mulOneAtAtime(6)(7) //调用
二、习题答案
12.1 编写函数values(fun:(Int)=>Int,low:Int,high:Int),该函数输出一个集合,对应给定区间内给定函数的输入和输出。比如,values(x=>x*x,-5,5)应该产出一个对偶的集合(-5,25),(-4,16),(-3,9),…,(5,25)
def values(fun: (Int)=>Int, low: Int, high: Int)={
var array = List[(Int, Int)]()
low to high foreach{
x => array = (x, fun(x)) :: array
}
array
}
println(values(x => x * x, -5,5).mkString(","))
----------
def values(fun: (Int)=> Int, low: Int, high: Int)= (low to high).map{x => (x,fun(x))}
val x = values(x => x * x, -5, 5)
println(x)
12.2 如何用reduceLeft得到数组中的最大元素?
val a = new Array[Int](10).map(x => util.Random.nextInt(10))
println(a.mkString(","))
val max = a.reduceLeft((x, y) => if (x > y) x else y)
println(max)
12.3 用to和reduceLeft实现阶乘函数,不得使用循环或递归
def fac(n: Int)= (0 to n).reduceLeft((a,b) => if(a== 0) 1 else(a * b))
for(i <- 0 to 9) {
println("%d %d".format(i, fac(i)))
}
12.4 前一个实现需要处理一个特殊情况,即n<1的情况。展示如何用foldLeft来避免这个需要。
scala> def factorial(n: Int) = (1 to n).foldLeft(1)(_ * _)
factorial: (n: Int)Int
scala> println(factorial(-3))
1
12.5 编写函数largest(fun:(Int)=>Int,inputs:Seq[Int]),输出在给定输入序列中给定函数的最大值。举例来说,largest(x=>10*x-x*x,1 to 10)应该返回25.不得使用循环或递归
def largest(fun: (Int)=> Int, inputs: Seq[Int])= inputs.map(fun(_)).max
println(largest(x => 10 * x - x*x, 1 to 10))
12.6 修改前一个函数,返回最大的输出对应的输入。举例来说,largestAt(fun:(Int)=>Int,inputs:Seq[Int])应该返回5。不得使用循环或递归
def largest(fun: (Int)=> Int, inputs: Seq[Int])= inputs.map(x => (x, fun(x))).reduceLeft((x, y) => if(x._2 > y._2)x else y)._1
println(largest(x => 10 * x - x*x, 1 to 10))
12.7 要得到一个序列的对偶很容易,比如:val pairs = (1 to 10) zip (11 to 20)
假定你想要对这个序列做某中操作—比如,给对偶中的值求和,但是你不能直接使用:
pairs.map(_ + )
函数 + _ 接受两个Int作为参数,而不是(Int,Int)对偶。编写函数adjustToPair,该函数接受一个类型为(Int,Int)=>Int的函数作为参数,并返回一个等效的, 可以以对偶作为参数的函数。举例来说就是:adjustToPair(_ * _)((6,7))应得到42。然后用这个函数通过map计算出各个对偶的元素之和
def ajustToPair(fun: (Int, Int)=> Int) = (x: (Int, Int)) => fun(x._1, x._2)
val x = ajustToPair(_ * _)(6, 7)
println(x)
val pairs = (1 to 10)zip (11 to 20)
println(pairs)
val y= pairs.map(ajustToPair(_ + _))
println(y)
12.8 在12.8节中,你看到了用于两组字符串数组的corresponds方法。做出一个对该方法的调用,让它帮我们判断某个字符串数组里的所有元素的长度是否和某个给定的整数数组相对应
val a = Array("Hello", "beautyful", "world","!")
val b = a.map(_.length)
println(a.mkString(","))
println(b.mkString(","))
val y = a.corresponds(b)(_.length == _)
println(y)
12.9 不使用柯里化实现corresponds。然后尝试从前一个练习的代码来调用。你遇到了什么问题?
def corresponds[A, B](a: Seq[A], b: Seq[B], f:(A, B)=> Boolean) = (a zip b).map(x => f(x._1, x._2)).count(!_)
val a = Array("Hello", "beautyful", "world","!")
val b = a.map(_.length)
println(a.mkString(","))
println(b.mkString(","))
val y = corresponds(a, b, (x: String, y: Int) => x.length == y)
println(y)
12.10 实现一个unless控制抽象,工作机制类似if,但条件是反过来的。第一个参数需要是换名调用的参数吗?你需要柯里化吗?
def unless(condition: => Boolean)(block: => Unit){ if(!condition) block }
unless(0 > 1) { println("Unless") } //需要换名和柯里化