作为值得函数
变量中可以存放函数import scala.math._ val num = 3.14 // Double
val fun = ceil _ // (Double) => Double, fun设为ceil函数, _ 意味着确实指这个函数,而不是碰巧忘记参数.
从技术上讲,_将ceil方法转成了函数,在scala中,无法直接操纵方法,而只能直接操纵函数.fun(num) // 4.0 调用他
Array(3.14, 1.42, 2.0).map(fun) // Array(4.0,2.0,2.0) 传递他
匿名函数
(x: Double) = >3 * x val triple = (x: Double) = >3 * x这就跟用def一样def triple(x: Double) = >3 * x Array(3.14, 1.42, 2.0).map((x: Double) = >3 * x) // 直接把匿名函数 传递给另一个函数
另一种写法,将函数参数包在花括号而不是圆括号中Array(3.14, 1.42, 2.0) map { (x: Double) = >3 * x
} // 没有句点, 使用中置表示法
带函数参数的函数
def valueAtOneQuarter(f: (Double) = >Double) = f(0.25) valueAtOneQuarter(ceil _) //1.0
valueAtOneQuarter(sqrt _) //0.5
valueAtOneQuarter的类型为 ((Double) = >Double) = >Double这种接受函数参数的函数,称之为高阶函数.高阶函数也可以产出另一个函数,def mulBy(factor: Double) = (x: Double) = >factor * x val quintuple = mulBy(5) quintuple(20) // 100
mulBy类型 (Double) = >((Double) = >Double)
参数(类型)推断
valueAtOneQuarter((x: Double) = >3 * x) // 0.75
由于valueAtOneQuarter知道会传入一个类型为 (Double) = >Double的函数,所以可以简写:valueAtOneQuarter((x) = >3 * x) valueAtOneQuarter(x = >3 * x) // 当只有一个参数的函数,可以略去参数外围的()
valueAtOneQuarter(3 * _) //如果参数在=>右侧只出现一次,可以用 _ 替换它
这些简写方式仅在参数类型已知的情况下才有效:val fun = 3 * _ //错误, 无法推断出类型
val fun = 3 * _(_: Double) // OK
val fun: (Double) = >Double = 3 * _ //OK,因为给出了fun的类型
一些常见的高阶函数
(1 to 9).map("*" * _).foreach(prinlnt _)(1 to 9).filter(_ % 2 == 0) // 2,4,6,8
reduceLeft方法接受一个二元的函数,即一个带有两个参数的函数,并将它应用到序列中的所有元素,从左到右 (1 to 9).reduceLeft(_ * _)等同于1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9更严格地说是 (... ((1 * 2) * 3) * ... * 9)排序"Marry has a little lamb".split(" ").sortWith(_.length < _.length)输出以个长度递增排序的数组: Array("a", "had", "Mary", "lamb", "little")
闭包
def mulBy(factor: Double) = (x: Double) = >factor * x val triple = mulBy(3) val half = mulBy(0.5) triple(14) + " " + half(14) // 42 17
triple和half都有自己的factor设置这样一个函数被称为闭包,闭包由代码和代码用到的任何非局部变量定义构成.
SAM 转换
在Java中,不支持函数作为参数传递,当要告诉另一个函数做某件事时,通常做法是蒋动作放在一个实现某接口的累中,然后将该类的一个实例传递给另一个方法很都时候,这些接口都只有单个抽象方法.在Java中被称为SAM类型
var counter = 0 val button = new JButton("Increment") button.addActionListener(new ActionListener {
override def actionPerformed(event: ActionEvent) {
counter += 1
}
})那么多样板代码,只为了执行一句counter += 1.如果我们提供一个隐式转换,就可以做只传一个函数给addActionListener,如:button.addActionListener((event: ActionEvent) = >counter += 1)隐式转换的声明:implicit def makAction(action: (ActionEvent) = >Unit) = new ActionListener {
override def actionPerformed(event: ActionEvent) {
action(event)
}
}只需把这个函数和上面的代码放一起,就可以在所有预期ActionListener对象的地方传入任何 (ActionEvent) = >Unit函数了.
柯里化
def mulOneAtATime(x: Int) = (y: Int) = >x * y mulOneAtATime(6)(7) Scala支持如下简写来定义这样的函数: def mulOneAtATime(x: Int)(y: Int) = x * y.这看似和直接定义两个参数,没有分别def mulOneAtATime(x: Int, y: Int) = x * y.但在Scala中有很实际的用途,有时候,需要柯里化来把某个函数的参数单独拿出来,以提供更多用于类型推断的信息.val a = Array("Hello", "World") val b = Array("Hello", "World") a.corresponds(b)(_.equalsIgnoreCase(_))这里,在API中def corresponds[B](that: Seq[B])(p: (A, B) = >Boolean) : Boolean这里,类型推断其可以分析出B出自that的类型,利用这个信息来分析作为参数p传入的函数.如,that是一个String类型的序列,那么久可以计算出p的类型为 (String, String) = >Boolean.然后编译时可以接受_.equalsIgnoreCase(_)作为 (a: String, b: String) = >a.equalsIgnoreCase(b)的简写了
无参函数作为参数的简化写法(漂亮写法,没有这些功能也能写,就是很丑而已)
def runInThread(block: () = >Unit) {
new Thread {
override def run() {
block()
}
}.start()
}
runInThread { () = >println("Hi")
} //比较难看
简化,省去参数声明和调用该函数的 () def runInThread(block: =>Unit) { // 省去(),俗称 换名调用参数.
new Thread {
override def run() {
block
} // 省去()
}.start()
}
runInThread {
println("Hi")
} //看上去更自然.
结合柯里化的样例:def until(condition: =>Boolean)(block: =>Unit) {
if (condition) {
block until(condition)(block)
}
}调用:
var x = 10 until(x == 0) {
x -= 1 println(x)
} // 完全像是在使用whili语句那样的函数.
如果没有柯里化的话:until(x == 0,
{...
}),就难看很多了
来源: