您的位置:首页 > 其它

scala 自学笔记 高阶函数

2015-02-17 11:15 295 查看
作为值得函数

变量中可以存放函数
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,{...}) , 就难看很多了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: