SlideShare une entreprise Scribd logo
1  sur  74
ScalaScala 中的函数与闭包中的函数与闭包
hongjiang 2013.4.23hongjiang 2013.4.23
说明说明
► 大纲大纲
1)1) 一等公民怎么体现一等公民怎么体现
2)2) 表达式表达式
3)3) 函数与方法函数与方法
4)4) 传值与传名传值与传名—— scalascala 中支持的参数传递方式中支持的参数传递方式
5)5) 高阶函数与柯里化高阶函数与柯里化
6)6) 偏应用函数偏应用函数
7)7) 偏函数偏函数
8)8) 一些谜题与细节一些谜题与细节
9)9) 闭包闭包
► 交流:交流: http://www.atatech.org/scalahttp://www.atatech.org/scala
旺旺群:旺旺群:
ScalaScala 交流和答疑交流和答疑 : 94329267,: 94329267, 密码:密码: sugsugsugsug
ScalaScala 布道会:布道会: 554375392554375392 面向有经验者面向有经验者
函数作为一等公民体现在哪儿?函数作为一等公民体现在哪儿?
first-class citizenfirst-class citizen
函数作为一等公民体现在哪儿?函数作为一等公民体现在哪儿?
1)1) 可传递可传递 // 赋值赋值
2)2) 高阶高阶
3)3) 嵌套函数和匿名函数嵌套函数和匿名函数
4)4) 闭包闭包
5)5) 偏应用偏应用 (partial application)(partial application)
参考: http://en.wikipedia.org/wiki/First-class_function
ScalaScala 中的表达式中的表达式
ScalaScala 中的表达式中的表达式
► 1)1) 所有的表达式都有值所有的表达式都有值
►2)2) 除了赋值和函数调用表达式,内置的几个除了赋值和函数调用表达式,内置的几个
表达式只有:表达式只有: if,while,for,try,matchif,while,for,try,match
►3)3) 块表达式块表达式 {{……}} 是基本表达式的组合,它的是基本表达式的组合,它的
值是最后一个表达式的值。值是最后一个表达式的值。
ScalaScala 中的表达式中的表达式
► 一些表达式的值:一些表达式的值:
1) a=1;1) a=1;
2) while(a<100){print(a)}2) while(a<100){print(a)}
3) if(a<2) 1;3) if(a<2) 1;
► 赋值表达式、赋值表达式、 whilewhile 表达式的值是表达式的值是 UnitUnit 类型类型 ,, 它的它的
值是唯一的值是唯一的 : (): ()
► ifif 表达式缺乏表达式缺乏 elseelse 的话,不符合条件则返回的话,不符合条件则返回 UnitUnit 类类
型的型的 ()() ;即上面相当于:;即上面相当于: if(a<2) 1 else ()if(a<2) 1 else ()
赋值语句的注意点:赋值语句的注意点:
不同于不同于 javajava ::
11 )) while( (line = readLine() ) != null )while( (line = readLine() ) != null )
不起作用,前边的不起作用,前边的 line = readLine()line = readLine() 得到的是得到的是
UnitUnit 类型值类型值
22 )) x=y=1; // y=1; x=()x=y=1; // y=1; x=()
y=1y=1 表达式的结果是表达式的结果是 ()() ,, xx 被赋予了被赋予了 UnitUnit 类型类型
的值的值
lambda: 函数字面量 (Function
literal)
(x :Int, y: Int) => x + y(x :Int, y: Int) => x + y
参数 函数体右箭头
产生一段匿名函数,类型为 (Int,Int)=>Int
Scala 中参数的个数为 0 到 22 个。
函数函数
1)1) Function vs MethodFunction vs Method
2)2) Functor ?Functor ?
Function,Method,FunctorFunction,Method,Functor
1)1) 狭义地区分狭义地区分 (( 从可传递性上从可传递性上 )) ::
方法方法 (method):(method): 指的是在指的是在 trait/class/objecttrait/class/object 中以中以 defdef 关键字声明的,它不能关键字声明的,它不能
被直接传递。被直接传递。
函数函数 (function):(function): 类型为类型为 ParamsType=>ResultTypeParamsType=>ResultType 的变量,这些变量背后的变量,这些变量背后
是用是用 FunctionNFunctionN 对象来封装的;可以被传递。方法可以转换为函数。对象来封装的;可以被传递。方法可以转换为函数。
2)2) 广义上,抛开背后的实现,方法就是函数;编译器某些场景自动把方法广义上,抛开背后的实现,方法就是函数;编译器某些场景自动把方法
封装为一个函数对象来传递。封装为一个函数对象来传递。 ScalaScala 社区并不特别区分这两个名词,注社区并不特别区分这两个名词,注
意语境,有时候函数就是指方法,有时候则是指函数对象意语境,有时候函数就是指方法,有时候则是指函数对象
3) Functor (3) Functor ( 函子函子 )) 是个高级概念,不可与是个高级概念,不可与 function,methodfunction,method 类比。类比。
暂简单理解为定义了暂简单理解为定义了 mapmap 方法的容器类型方法的容器类型
函数与方法函数与方法
►定义方法:定义方法:
def foo(i:Int) :Int = { i+2 }def foo(i:Int) :Int = { i+2 }
方法返回值类型不为方法返回值类型不为 UnitUnit 时,时, == 号不可省略号不可省略
def foo():Unit = {println(def foo():Unit = {println(““hihi””)})} 等同于等同于
def foo() {println(def foo() {println(““hihi””) }) }
返回类型为返回类型为 UnitUnit 时,时, == 号可以省略,返回值号可以省略,返回值
类型也可以省略。类型也可以省略。
函数与方法函数与方法
► 函数函数 (( 值值 )) 的类型的类型
(( 入参类型入参类型 ) =>) => 出参类型出参类型
如同类与实例,函数类型是对函数的抽象。如同类与实例,函数类型是对函数的抽象。
每个通过每个通过 defdef 定义的方法本身并无类型之说,但被封装成的函数对象是定义的方法本身并无类型之说,但被封装成的函数对象是
有类型的,一些语境里说的方法类型也是指其函数类型有类型的,一些语境里说的方法类型也是指其函数类型
注意:注意:
定义方法时参个数可以多于定义方法时参个数可以多于 2222 个个
但函数只接受但函数只接受 00 到到 2222 个参数。个参数。
超过超过 2222 个参数的方法无法被封装为函数对象个参数的方法无法被封装为函数对象
函数与方法函数与方法
► 函数函数 (( 值值 )) 的类型的类型
def foo() =def foo() = ““hihi””
类型为类型为 : () => String //: () => String // 这里的这里的 ()() 表示空表示空
def foo() {println(def foo() {println(““hihi””)})}
类型为类型为 : () => Unit: () => Unit
def foo(x:Int,y:Int) = x+ydef foo(x:Int,y:Int) = x+y
类型为类型为 : (Int,Int) => Int: (Int,Int) => Int
函数与方法函数与方法
► 对函数变量赋值对函数变量赋值 ::
1)1) 对变量明确的声明函数类型对变量明确的声明函数类型
val f:Int=>Int = fooval f:Int=>Int = foo
a) fooa) foo 是值是值 (( 函数对象函数对象 )) ,直接赋值。,直接赋值。
b) foob) foo 是方法,会被编译器封装为一个是方法,会被编译器封装为一个 Function1[Int,Int]Function1[Int,Int] 对象赋给对象赋给 ff
2)2) 通过字面量通过字面量 (lambda)(lambda) ,创建一个匿名函数赋值给变量,创建一个匿名函数赋值给变量
val f2 = (x:Int)=>x+1val f2 = (x:Int)=>x+1
编译器会推导出编译器会推导出 f2f2 的类型:的类型: Int=>IntInt=>Int
3)3) 部分应用部分应用
def foo(s:String) = {def foo(s:String) = {……}}
val f3 = foo _val f3 = foo _
函数与方法函数与方法
在执行上:在执行上:
1)1) 方法的执行与方法的执行与 javajava 里的方法执行一致。里的方法执行一致。
2)2) 函数的执行,实际是对相应的函数的执行,实际是对相应的 FunctionFunction 对对
象调用其象调用其 applyapply 方法。方法。
3)3) 函数的执行效率要低于方法。函数的执行效率要低于方法。
函数与方法函数与方法
returnreturn 关键字:关键字:
1)1) 在方法中返回时可用可不用,但非最后一行返回必在方法中返回时可用可不用,但非最后一行返回必
须用须用 returnreturn
2)2) 通过字面量声明一个函数时,不能使用通过字面量声明一个函数时,不能使用 returnreturn
val a = (x:Int)=>{return x+1} //errorval a = (x:Int)=>{return x+1} //error
函数与方法函数与方法
方法不能被直接传递方法不能被直接传递 ??
scala> def f1() = {println(1)}scala> def f1() = {println(1)}
scala> def f2(f: ()=>Unit) = {f()}scala> def f2(f: ()=>Unit) = {f()}
scala> f2(f1) //scala> f2(f1) // 这里不是直接传递方法么?这里不是直接传递方法么?
tips:tips: 辅助工具辅助工具
scalac -Xshow-phasesscalac -Xshow-phases
列出所有的编译过程的各个象限,通常对我们有帮助列出所有的编译过程的各个象限,通常对我们有帮助
的是的是 typertyper 和和 jvmjvm
当拿不准一段代码最终被翻译为什么时,可通过当拿不准一段代码最终被翻译为什么时,可通过
scalacscalac ––Xprint:typerXprint:typer 或或 scalacscalac ––Xprint:jvmXprint:jvm
来看看代码被翻译成了什么来看看代码被翻译成了什么
也可以直接当脚本来调试,也可以直接当脚本来调试, egeg ::
$ scala$ scala ––Xprint:typerXprint:typer ––ee ‘‘val a=2val a=2’’
函数与方法函数与方法
$ scala -Xprint:typer -e 'def f1()={println(1)}; def f2(f:()=>Unit){f1()}; f2(f1)$ scala -Xprint:typer -e 'def f1()={println(1)}; def f2(f:()=>Unit){f1()}; f2(f1)‘‘
…………
private def f1(): Unit = scala.this.Predef.println(111);private def f1(): Unit = scala.this.Predef.println(111);
private def f2(f: () => Unit): Unit = $anon.this.f1();private def f2(f: () => Unit): Unit = $anon.this.f1();
$anon.this.f2({$anon.this.f2({
(() => $anon.this.f1())(() => $anon.this.f1())
})})
…………
看到看到 f2(f1)f2(f1) 在调用的时候被转成了在调用的时候被转成了 f2( ()=>f1() )f2( ()=>f1() )
参数参数 f1f1 被封装成了一个函数对象被封装成了一个函数对象
函数与方法函数与方法
无参函数无参函数 ::
scala> def foo = {println(scala> def foo = {println(““hihi””)})}
scala> fooscala> foo
hihi
在定义和执行时都可以省略小括号。在定义和执行时都可以省略小括号。
统一访问原则统一访问原则 (uniform access principle)(uniform access principle) ::
客户代码不应由属性是通过字段实现还是方法实现而受影响。客户代码不应由属性是通过字段实现还是方法实现而受影响。
定义无参函数的惯例定义无参函数的惯例 ::
方法没有参数方法没有参数 &&&& 方法不改变可变状态方法不改变可变状态 (( 无副作用无副作用 ))
函数与方法函数与方法
无参函数:无参函数:
原则上,原则上, scalascala 中的函数都可以省略空括号,然而在中的函数都可以省略空括号,然而在
调用的方法超出其调用者对象的属性时,推荐仍保调用的方法超出其调用者对象的属性时,推荐仍保
持空括号。持空括号。
比如,方法执行了比如,方法执行了 I/O,I/O, 或有改变可变状态,或读取了或有改变可变状态,或读取了
不是调用者字段的不是调用者字段的 varvar ,总之无论是直接还是非直,总之无论是直接还是非直
接的使用可变对象都应该加空括号接的使用可变对象都应该加空括号
"hello".length //"hello".length // 没有副作用,可以省略括号没有副作用,可以省略括号
println() //println() // 最好别省略最好别省略 ()()
函数与方法函数与方法
返回值为函数的函数:返回值为函数的函数:
scala> def hf = (x:Int) => x+1scala> def hf = (x:Int) => x+1
hf: Int => Inthf: Int => Int
scala> hfscala> hf //// 执行结果是一个函数对象执行结果是一个函数对象
res0: Int => Int = <function1>res0: Int => Int = <function1>
scala> hf(2)scala> hf(2)
res2: Int = 3res2: Int = 3
相当于两次调用,第一次先得到一个函数对象,然后在对这个函数对象传相当于两次调用,第一次先得到一个函数对象,然后在对这个函数对象传
参调用参调用
有名参数与缺省参数有名参数与缺省参数
有名参数有名参数 (named argments)(named argments)
提高可读性提高可读性
scala> def printName(first:String, last:String) {scala> def printName(first:String, last:String) {
println(first + " " + last)println(first + " " + last)
}}
scala> printName(last="wang", first="hongjiang")scala> printName(last="wang", first="hongjiang")
hongjiang wanghongjiang wang
缺省参数缺省参数 (default arguments)(default arguments)
► scala> def greeting(name:String, msg:String = "hi”){scala> def greeting(name:String, msg:String = "hi”){
println(msg+","+name)println(msg+","+name)
}}
► scala> greeting("hongjiang")scala> greeting("hongjiang")
hi,hongjianghi,hongjiang
缺省参数缺省参数 (default arguments)(default arguments)
►缺省参数的一个适用场景:提供缺省参数的一个适用场景:提供 copycopy 方法时方法时
scala> class A(val a:String,val b:String) {scala> class A(val a:String,val b:String) {
def copy(a:String="AAA",b:String="BBB") = new A(a,b)def copy(a:String="AAA",b:String="BBB") = new A(a,b)
}}
scala> val a = new A("a","b")scala> val a = new A("a","b")
scala> val b = a.copy()scala> val b = a.copy()
scala> b.ascala> b.a
res3: String = AAAres3: String = AAA
case classcase class 里的里的 copycopy 方法是由编译器生成的,使用了缺省参数。方法是由编译器生成的,使用了缺省参数。
参数的传递参数的传递
传值 ? 传址? 还是?传值 ? 传址? 还是?
CTMCTM 中定义的中定义的 66 种传值方式种传值方式
11 Call by referenceCall by reference
22 Call by varible //1Call by varible //1 的特例的特例
33 Call by value-result //2Call by value-result //2 的变种的变种
44 Call by valueCall by value //Java//Java 中只支持这一种中只支持这一种
55 Call by nameCall by name //Scala//Scala 中支持中支持
66 Call by needCall by need //5//5 的变种的变种
注:注:
1)1) 引用自:引用自: http://blog.csdn.net/sunqihui/article/details/5597995http://blog.csdn.net/sunqihui/article/details/5597995
2) CTM:2) CTM: 《计算机编程的概念、技术与模型》《计算机编程的概念、技术与模型》 http://book.douban.com/subject/1782316/http://book.douban.com/subject/1782316/
传值还是传名传值还是传名
对比两段函数:对比两段函数:
片段片段 1:1:
scala> def foo(f : ()=>String) {scala> def foo(f : ()=>String) {
println("111");println("111");
println(f()+println(f()+““okok””) //) // 写为写为 ff 会如何?会如何?
}}
scala> foo( {println("AAA"); ()=>"AAA"} )scala> foo( {println("AAA"); ()=>"AAA"} )
传值?传名?结果是什么?传值?传名?结果是什么?
传值还是传名传值还是传名
片段片段 22 ::
scala> def bar(f : =>String) { //scala> def bar(f : =>String) { // 省略了小括号省略了小括号
println("111");println("111");
println(f +println(f + ““okok””) //f) //f 而不是而不是 f()f()
}}
scala> bar( {println("AAA"); "AAA"} )scala> bar( {println("AAA"); "AAA"} )
传值?传名?结果是什么?传值?传名?结果是什么?
传值还是传名传值还是传名
foo( {println("AAA"); ()=>"AAA"} )foo( {println("AAA"); ()=>"AAA"} )
传递的是表达式的结果,表达式被执行 / 求值
bar( {println("AAA"); "AAA"} )bar( {println("AAA"); "AAA"} )
传递的是表达式,而非结果,这里不被执行
传值还是传名传值还是传名
►1) by name1) by name 传递只出现在函数参数中传递只出现在函数参数中
►2)2) 同上,同上, =>R=>R 类型只能出现在函数参数中类型只能出现在函数参数中
表示表示 by name parameterby name parameter
►3) => R3) => R 不能单纯看作是不能单纯看作是 ()=>R()=>R 的缩写,两的缩写,两
者传递形式不同者传递形式不同
高阶函数高阶函数
高阶函数高阶函数
► higher-order function:higher-order function:
以其他函数做参数的函数。以其他函数做参数的函数。 eg:eg:
scala> def f2(f: ()=>Unit) { f() }scala> def f2(f: ()=>Unit) { f() }
f2: (f: () => Unit)Unitf2: (f: () => Unit)Unit
scala> def f1() {println(1)}scala> def f1() {println(1)}
f1: ()Unitf1: ()Unit
scala> f2(f1)scala> f2(f1)
11
scala> f2(()=>println(scala> f2(()=>println(““hihi””)) //)) // 传入匿名函数传入匿名函数
hihi
高阶函数高阶函数
► 一些集合中的高阶函数的例子:一些集合中的高阶函数的例子:
scala> val a = Array(1,2,3)scala> val a = Array(1,2,3)
a: Array[Int] = Array(1, 2, 3)a: Array[Int] = Array(1, 2, 3)
scala> a.map(_ + 1) // x=>x+1scala> a.map(_ + 1) // x=>x+1
res3: Array[Int] = Array(2, 3, 4)res3: Array[Int] = Array(2, 3, 4)
scala> a.filter(_ > 2) // x=>x>2scala> a.filter(_ > 2) // x=>x>2
res9: Array[Int] = Array(3)res9: Array[Int] = Array(3)
柯里化柯里化 (currying)(currying)
柯里化柯里化 (currying)(currying)
scala> def sum(x:Int, y:Int) = x+y
sum: (x: Int, y: Int)Int
// 参数打散,两个参数分开
scala> def sum2(x:Int)(y:Int) = x+y
sum2: (x: Int)(y: Int)Int
柯里化柯里化 (currying)(currying)
scala> sum2(1)(2)
res1: Int = 3
// 上面的调用相当于下面的几个步骤
scala> def first(x:Int) = (y:Int)=>x+y
first: (x: Int)Int => Int
scala> first(1)
res2: Int => Int = <function1>
scala> val second = first(1)
second: Int => Int = <function1>
scala> second(2)
res3: Int = 3
柯里化柯里化 (currying)(currying)
► 函数链函数链
把一个带有多个参数的函数,转换为多个把一个带有多个参数的函数,转换为多个
只有一个参数的函数来执行只有一个参数的函数来执行
f(1)(2)(3)f(1)(2)(3)  ((((f(1)f(1)))(2)(2)))(3)(3)
fa(1) fb(2) fc(3)
产生新的函数
带入参数 1 执行
产生新的函数
x
得到最终的值
带入参数 2 执行 带入参数 3 执行
柯里化柯里化 (currying)(currying)
► 柯理化的意义?柯理化的意义?
控制抽象,可改变代码的书写风格。控制抽象,可改变代码的书写风格。
1) foo(res, ()=>print(1) foo(res, ()=>print(““test))test))
2) foo(res)(()=>print(2) foo(res)(()=>print(““testtest””))))
3) foo(res){3) foo(res){
()=>print(()=>print(““testtest””))
}}
柯里化柯里化 (currying)(currying)
►模拟模拟 C#C# 里的里的 usingusing 自动释放资源的写法:自动释放资源的写法:
using(res) {using(res) {
handle(handle(……))
}}
resres 会被自动释放会被自动释放
resres 必须是必须是 IDisposeableIDisposeable 接口子类接口子类
柯里化柯里化 (currying)(currying)
►模拟模拟 C#C# 里的里的 usingusing
ScalaScala 里的实现里的实现
//// 暂忽略类型表达暂忽略类型表达
def using[C <% { def close(): Unit }, T](resource: C)(handle: C => T): T = {def using[C <% { def close(): Unit }, T](resource: C)(handle: C => T): T = {
try {try {
handle(resource)handle(resource)
} finally {} finally {
closeQuietly(resource)closeQuietly(resource)
}}
}}
柯里化柯里化 (currying)(currying)
►模拟模拟 C#C# 里的里的 usingusing
//// 使用使用 usingusing
using(Source.fromURL(cl.getResource(file))) {using(Source.fromURL(cl.getResource(file))) {
stream => {stream => {
for (line <- stream.getLines){for (line <- stream.getLines){……}}
}}
resultresult
}}
偏应用函数偏应用函数 (partial application function)(partial application function)
也被翻译部分应用函数也被翻译部分应用函数
把一个函数适配为另一个函数把一个函数适配为另一个函数
偏应用函数偏应用函数 (partial application function)(partial application function)
占位符:占位符: __
scala> def pow(x:Int, y:Int) = Math.pow(x,y)scala> def pow(x:Int, y:Int) = Math.pow(x,y)
pow: (x: Int, y: Int)Doublepow: (x: Int, y: Int)Double
scala> pow(2,3)scala> pow(2,3)
res4: Double = 8.0res4: Double = 8.0
scala> val square = pow(_:Int, 2)scala> val square = pow(_:Int, 2)
square: Int => Double = <function1>square: Int => Double = <function1>
scala> square(3)scala> square(3)
res5: Double = 9.0res5: Double = 9.0
偏应用函数偏应用函数 (partial application function)(partial application function)
scala> def log(time:Date, msg:String) { println(time + ": " + msg) }scala> def log(time:Date, msg:String) { println(time + ": " + msg) }
log: (time: java.util.Date, msg: String)Unitlog: (time: java.util.Date, msg: String)Unit
scala> val log2 = log(new Date, _:String)scala> val log2 = log(new Date, _:String)
log2: String => Unit = <function1>log2: String => Unit = <function1>
scala> log2("test1")scala> log2("test1")
scala> log2("test2")scala> log2("test2")
scala> log2("test3")scala> log2("test3")
三次时间一样吗?三次时间一样吗?
绑定的是表达式,还是表达式的结果?绑定的是表达式,还是表达式的结果?
偏应用函数偏应用函数 (partial application function)(partial application function)
不绑定任何参数不绑定任何参数
scala> val pow2 = pow _scala> val pow2 = pow _
pow2: (Int, Int) => Double = <function2>pow2: (Int, Int) => Double = <function2>
注意:一些书和资料里对接收函数类型参数的地方,传递时需注意:一些书和资料里对接收函数类型参数的地方,传递时需
要显式的把方法转换为函数对象要显式的把方法转换为函数对象 :: 通过通过 __ 可以快速实现。可以快速实现。
那可能是较低版本的编译器。那可能是较低版本的编译器。
最近版本的编译器已不需要,发现是方法会自动封装成一个函最近版本的编译器已不需要,发现是方法会自动封装成一个函
数。数。
偏函数偏函数 (partial function)(partial function)
注意和偏应用函数是两回事
偏函数偏函数 (partial function)(partial function)
图片来源图片来源 : http://developer.51cto.com/art/200912/166875.htm: http://developer.51cto.com/art/200912/166875.htm
也被翻译为也被翻译为““部分函数部分函数””,区分于,区分于““完全函数完全函数””
从数学上说,偏函数就是只实现了部分映射的函数:从数学上说,偏函数就是只实现了部分映射的函数:
偏函数偏函数 (( 列子列子 ))
1)1) 草原很好的解释了草原很好的解释了““偏函数偏函数””的概念以及用途:的概念以及用途:
https://groups.google.com/forum/?fromgroups=#!https://groups.google.com/forum/?fromgroups=#!
topic/scalacn/ASo80yip9fAtopic/scalacn/ASo80yip9fA
2)2) 必须声明为必须声明为 PartialFunctionPartialFunction ;主要通过模式匹配;主要通过模式匹配
来实现来实现
偏函数的组合偏函数的组合
通过通过 andThenandThen 或或 orElseorElse 来组合偏函数:来组合偏函数:
scala> def p1:PartialFunction[String,String] = {scala> def p1:PartialFunction[String,String] = {
case "A" => "OK"case "A" => "OK"
}}
scala> def p3:PartialFunction[String,String] = {scala> def p3:PartialFunction[String,String] = {
case "OK" => "haha"case "OK" => "haha"
}}
scala> (p1 andThen p3)("A")   //  A => OK => hahascala> (p1 andThen p3)("A")   //  A => OK => haha
res3: String = hahares3: String = haha
一些谜题一些谜题 (puzzles)(puzzles)
一些谜题一些谜题
定义方法时省略小括号定义方法时省略小括号 ::
scala> def m = "hi"scala> def m = "hi"
m: String //m: String // 为什么不是为什么不是 ()String()String
scala> val f:()=>String = m //scala> val f:()=>String = m // 会如何?会如何?
scala> def m() = "hi”scala> def m() = "hi”
scala> val f:()=>String = m //scala> val f:()=>String = m // 又会如何?又会如何?
怎么理解?怎么理解?
一些谜题一些谜题
定义方法时省略小括号定义方法时省略小括号 ::
scala> class MyClass { def apply() { println("my class") } }scala> class MyClass { def apply() { println("my class") } }
scala> def foo = new MyClassscala> def foo = new MyClass
scala> fooscala> foo
scala> foo() //scala> foo() // 结果是什么?结果是什么?
// foo// foo 与与 foo()foo() 的差异?的差异?
一些谜题一些谜题
►scalascala 里里
 def foo() = xxxdef foo() = xxx 在调用时可以省略在调用时可以省略 ff 后边的后边的 ()()
 但定义时如果不带小括号但定义时如果不带小括号 def foo = xxxdef foo = xxx 则调用则调用
时加时加 ()() 要注意,要注意, foo()foo() 被翻译为了被翻译为了 (foo).apply()(foo).apply()
一些谜题一些谜题
►UnitUnit 的问题:的问题:
1)1) 为何不用为何不用 javajava 的的 VoidVoid 类型,而引入类型,而引入 UnitUnit 类类
型?型?
a)a) 类型一致性类型一致性
b) voidb) void 是面向函数的,是面向函数的, unitunit 除了可以是函数返回类型也可以除了可以是函数返回类型也可以
是变量的类型。另,每个表达式是变量的类型。另,每个表达式 // 语句都有值,一些表达式语句都有值,一些表达式
的值为的值为 unitunit
一些谜题一些谜题
Unit 的问题:
val a = () => Unit // aval a = () => Unit // a 是什么类型?是什么类型?
val b = () => {} //val b = () => {} // 有什么不同?有什么不同?
注意:第一个匿名函数中的注意:第一个匿名函数中的 UnitUnit 是伴生对象是伴生对象
一些谜题一些谜题
UnitUnit 的问题:的问题:
1)1)def foo(f: Unit)def foo(f: Unit)
2)2)def foo(f: =>Unit)def foo(f: =>Unit)
3)3)def foo(f: ()=>Unit)def foo(f: ()=>Unit)
不同在哪儿?不同在哪儿?
一些谜题一些谜题
UnitUnit 的问题:的问题:
1)1)def foo(f: Unit)def foo(f: Unit)
2)2)def foo(f: =>Unit)def foo(f: =>Unit)
对上面的方法传入对上面的方法传入 foo(2), foo(2,3,”4”) ?foo(2), foo(2,3,”4”) ?
关于使用关于使用 unitunit 做参数的讨论:做参数的讨论:
http://www.atatech.org/qa/detail/13423?group_id=51http://www.atatech.org/qa/detail/13423?group_id=51
闭包闭包 (closure)(closure)
闭包闭包
跟函数有什么不同么?跟函数有什么不同么?
闭包的本质:代码块闭包的本质:代码块 ++ 上下文上下文
关于引用环境的绑定关于引用环境的绑定 (The Binding of(The Binding of
Referencing Environments)Referencing Environments) ,,
先通过一个先通过一个 javajava 的匿名内部类来看:的匿名内部类来看:
JavaJava 中的匿名内部类如何访问局部变量中的匿名内部类如何访问局部变量
public Thread createThread(){public Thread createThread(){
//// 提升局部变量的生命周期提升局部变量的生命周期
finalfinal int innerVar = 100;int innerVar = 100;
return new Thread(){return new Thread(){
public void run(){public void run(){
System.out.println(innerVar);System.out.println(innerVar);
}}
};};
}}
innerVarinnerVar 还是分配在栈空间上么?还是分配在栈空间上么?
JavaJava 的匿名内部类,和闭包很像。但用匿名内部类来实现,前的匿名内部类,和闭包很像。但用匿名内部类来实现,前
提是先要定义好该行为的接口。繁琐一些,不那么灵活提是先要定义好该行为的接口。繁琐一些,不那么灵活
闭包闭包
► 闭包不可序列化闭包不可序列化
► 要避免可变状态要避免可变状态
一个一个 javascriptjavascript 的例子的例子
var div = document.getElementById("testDiv");var div = document.getElementById("testDiv");
var events = {onclick: "clicked",var events = {onclick: "clicked",
onchange: "changed",onchange: "changed",
onmouseover: "mouse over"};onmouseover: "mouse over"};
for(for(ee in events){in events){
div[e] = function(){div[e] = function(){
alert(events[e]);alert(events[e]);
};};
}}
解决方式:多一层抽象解决方式:多一层抽象
for(e in events){for(e in events){
div[e] = function(div[e] = function(e){){
return function(){return function(){
alert(events[e]);alert(events[e]);
};};
}(e);}(e);
}}
每次绑定不同的局部对象。每次绑定不同的局部对象。
多加的这层函数叫做因子函数多加的这层函数叫做因子函数 (factor function)(factor function)
rubyruby 的见:的见:
http://www.javaeye.com/topic/156337http://www.javaeye.com/topic/156337
闭包:更深入的了解闭包:更深入的了解
The Binding of Referencing EnvironmentsThe Binding of Referencing Environments
(( 引用环境的约束引用环境的约束 ))
在递归的情况会是怎样的?在递归的情况会是怎样的?
闭包的早绑定和晚绑定闭包的早绑定和晚绑定 (( 深约束深约束 // 浅约浅约
束束 ))
program binding_example(input, output);program binding_example(input, output);
procedure A(I : integer; procedure P);procedure A(I : integer; procedure P);
procedure B; //procedure B; // 子函数子函数 BB
beginbegin
writeln(I);writeln(I);
end;end;
begin (* A *)begin (* A *)
if I > 1 thenif I > 1 then
PP
elseelse
A(2,B); //A(2,B); // 递归递归
end;end;
procedure C; begin end;procedure C; begin end;
begin (* main *)begin (* main *)
A(1, C);A(1, C);
endend
PascalPascal 里的深约束。在通过形式参数里的深约束。在通过形式参数 PP 调调
用用 BB 时,存在着时,存在着 II 的两个实例。由于的两个实例。由于 PP 的的
闭包是在闭包是在 AA 的第一个调用中创建的,因此的第一个调用中创建的,因此
它使用该调用时的那个它使用该调用时的那个 II ,因此打印出,因此打印出
11 。。
------------------------
以上摘自《程序设计语言以上摘自《程序设计语言————实践之路》第二版,引用环境的约束一节。实践之路》第二版,引用环境的约束一节。
下面是一个同事把上面的代码翻译为下面是一个同事把上面的代码翻译为 C#C# ,看看是不是一样,他的回复:,看看是不是一样,他的回复:
► 确实确实 C#C# 也是这样。不过用也是这样。不过用 C#C# 的语法写出来的代码,看上去结果比的语法写出来的代码,看上去结果比 PascalPascal 要明显一些,要明显一些,
我觉得。我觉得。
              static void A(int i, Action p)static void A(int i, Action p)
              {{
                     if (i > 1)if (i > 1)
                            p();p();
                     elseelse
                            A(2, () => Console.WriteLine(i));A(2, () => Console.WriteLine(i));
              }}
► C#C# 不允许函数嵌套,只允许函数嵌套不允许函数嵌套,只允许函数嵌套 closureclosure ,当然也可以写成:,当然也可以写成:
              static void B(int i)static void B(int i)
              {{
                     Console.WriteLine(i);Console.WriteLine(i);
              }}
              static void A(int i, Action p)static void A(int i, Action p)
              {{
                     if (i > 1)if (i > 1)
                            p();p();
                     elseelse
                            A(2, () => B(i));A(2, () => B(i));
              }}
► 结果也没有差别,其实前面那种写法结果也没有差别,其实前面那种写法 Console.WriteLineConsole.WriteLine 就是就是 BB 。。
这两种写法看上去结果都是很明显的,调用这两种写法看上去结果都是很明显的,调用 WriteLineWriteLine 的那个的那个 ii 只能是只能是
11 。。
PascalPascal 的的 closureclosure 依赖的是帧(一个函数调用发生时的栈信息)指针的依赖的是帧(一个函数调用发生时的栈信息)指针的
传递,所有变量都还是存在于栈上;而传递,所有变量都还是存在于栈上;而 C#C# 是靠把是靠把 closureclosure 中用到的变量中用到的变量
包装进一个(匿名)类,包装进一个(匿名)类, closureclosure 本身则是该类的一个方法。本身则是该类的一个方法。
PascalPascal 的基于帧的实现是所有试图实现的基于帧的实现是所有试图实现 closureclosure 而又没有自动垃圾回收而又没有自动垃圾回收
机制的语言的无奈之举,这种方法不仅看上去比较费解,而且在应用上机制的语言的无奈之举,这种方法不仅看上去比较费解,而且在应用上
也有限制--用帧实现的也有限制--用帧实现的 closureclosure 难以在多线程环境中使用,因为难以在多线程环境中使用,因为
closureclosure 中的变量存在于栈上,中的变量存在于栈上, closureclosure 能否得到执行完全取决于构造能否得到执行完全取决于构造
closureclosure 的那个函数是否已经返回,也就是说,构造的那个函数是否已经返回,也就是说,构造 closureclosure 的函数必须的函数必须
等待,知道等待,知道 closureclosure 执行完毕才能返回。执行完毕才能返回。
比如比如 C#C# 中中 closureclosure 经常被用于执行一些异步调用,如果是基于帧的经常被用于执行一些异步调用,如果是基于帧的
closureclosure 在这些方面就很难得到有效应用了。在这些方面就很难得到有效应用了。
闭包的早绑定和晚绑定闭包的早绑定和晚绑定
►ScalaScala 里的实现:里的实现:
scala> def a(i:Int, f: =>Unit) {scala> def a(i:Int, f: =>Unit) {
def b() {println(i)} //def b() {println(i)} // 嵌套函数嵌套函数
if (i>1) f else a(2,b) //if (i>1) f else a(2,b) // 递归递归
}}
scala> a(1, {})scala> a(1, {})
参考参考
http://delicious.com/w.hongjiang/closureshttp://delicious.com/w.hongjiang/closures
http://james-iry.blogspot.comhttp://james-iry.blogspot.com
http://twitter.github.io/effectivescala/http://twitter.github.io/effectivescala/
http://news.cnblogs.com/n/175549/http://news.cnblogs.com/n/175549/
http://www.yinwang.org/blog-cn/2013/04/02/currying/
《程序设计语言《程序设计语言————实践之路》第二版实践之路》第二版
Follow me:Follow me:
http://weibo.com/woodcafehttp://weibo.com/woodcafe
https://twitter.com/woodcafehttps://twitter.com/woodcafe
http://www.laiwang.com/u/3001http://www.laiwang.com/u/3001
http://www.slideshare.net/hongjiang //ppthttp://www.slideshare.net/hongjiang //ppt 和和 pdfpdf
http://hongjiang.info (http://hongjiang.info ( 内容后续放入内容后续放入 ))

Contenu connexe

Tendances

Implantable collamer lens(ICL)
Implantable collamer lens(ICL)Implantable collamer lens(ICL)
Implantable collamer lens(ICL)Samuel Ponraj
 
Intracorneal ring-segments-by-femtosecond-tunnel-creation
Intracorneal ring-segments-by-femtosecond-tunnel-creationIntracorneal ring-segments-by-femtosecond-tunnel-creation
Intracorneal ring-segments-by-femtosecond-tunnel-creationAmr Mounir
 
IOL power calculation special situations
IOL power calculation special situations IOL power calculation special situations
IOL power calculation special situations Laxmi Eye Institute
 
Utilizing topolyzer vario & oculyzer ii for accurate refractive
Utilizing topolyzer vario & oculyzer ii for accurate refractiveUtilizing topolyzer vario & oculyzer ii for accurate refractive
Utilizing topolyzer vario & oculyzer ii for accurate refractiveMichael Mrochen
 
cataract ,history,ECCE AND SICS.pptx
cataract ,history,ECCE AND SICS.pptxcataract ,history,ECCE AND SICS.pptx
cataract ,history,ECCE AND SICS.pptxrameshbhandari32
 
FRCS - A journey with a past candidate!
FRCS - A journey with a past candidate!FRCS - A journey with a past candidate!
FRCS - A journey with a past candidate!DrMahmoud Medhat
 
PHYSIOLOGY OF AQUEOUS HUMOUR & IOP REGULATION 3.pptx
PHYSIOLOGY OF AQUEOUS HUMOUR & IOP REGULATION 3.pptxPHYSIOLOGY OF AQUEOUS HUMOUR & IOP REGULATION 3.pptx
PHYSIOLOGY OF AQUEOUS HUMOUR & IOP REGULATION 3.pptxAkashChaurewar1
 
Limbal stem cell Deficiency; amniotic membrane transplantation
Limbal stem cell Deficiency; amniotic membrane transplantationLimbal stem cell Deficiency; amniotic membrane transplantation
Limbal stem cell Deficiency; amniotic membrane transplantationOm Patel
 
Laser in Ophthalmology
Laser in OphthalmologyLaser in Ophthalmology
Laser in Ophthalmologyanjoichi
 
Introduction to phaco operation
Introduction to phaco operationIntroduction to phaco operation
Introduction to phaco operationAbdelmonem Hamed
 
Postoperative Complication of DALK
Postoperative Complication of DALKPostoperative Complication of DALK
Postoperative Complication of DALKDiyarAlzubaidy
 
Cómo presentar la tracción de tu negocio a posibles inversionistas
Cómo presentar la tracción de tu negocio a posibles inversionistasCómo presentar la tracción de tu negocio a posibles inversionistas
Cómo presentar la tracción de tu negocio a posibles inversionistasVTRNegocios
 
AS OCT & UBM - Dr Shylesh B Dabke
AS OCT & UBM - Dr Shylesh B DabkeAS OCT & UBM - Dr Shylesh B Dabke
AS OCT & UBM - Dr Shylesh B DabkeShylesh Dabke
 
RSpec on Rails Tutorial
RSpec on Rails TutorialRSpec on Rails Tutorial
RSpec on Rails TutorialWen-Tien Chang
 
Patch Graft
Patch GraftPatch Graft
Patch GraftEBAI
 

Tendances (20)

Implantable collamer lens(ICL)
Implantable collamer lens(ICL)Implantable collamer lens(ICL)
Implantable collamer lens(ICL)
 
Uveitic glaucoma.pptx
Uveitic glaucoma.pptxUveitic glaucoma.pptx
Uveitic glaucoma.pptx
 
Intracorneal ring-segments-by-femtosecond-tunnel-creation
Intracorneal ring-segments-by-femtosecond-tunnel-creationIntracorneal ring-segments-by-femtosecond-tunnel-creation
Intracorneal ring-segments-by-femtosecond-tunnel-creation
 
IOL power calculation special situations
IOL power calculation special situations IOL power calculation special situations
IOL power calculation special situations
 
Utilizing topolyzer vario & oculyzer ii for accurate refractive
Utilizing topolyzer vario & oculyzer ii for accurate refractiveUtilizing topolyzer vario & oculyzer ii for accurate refractive
Utilizing topolyzer vario & oculyzer ii for accurate refractive
 
cataract ,history,ECCE AND SICS.pptx
cataract ,history,ECCE AND SICS.pptxcataract ,history,ECCE AND SICS.pptx
cataract ,history,ECCE AND SICS.pptx
 
Fungel keratitis date 15
Fungel keratitis date 15Fungel keratitis date 15
Fungel keratitis date 15
 
FRCS - A journey with a past candidate!
FRCS - A journey with a past candidate!FRCS - A journey with a past candidate!
FRCS - A journey with a past candidate!
 
PHYSIOLOGY OF AQUEOUS HUMOUR & IOP REGULATION 3.pptx
PHYSIOLOGY OF AQUEOUS HUMOUR & IOP REGULATION 3.pptxPHYSIOLOGY OF AQUEOUS HUMOUR & IOP REGULATION 3.pptx
PHYSIOLOGY OF AQUEOUS HUMOUR & IOP REGULATION 3.pptx
 
Limbal stem cell Deficiency; amniotic membrane transplantation
Limbal stem cell Deficiency; amniotic membrane transplantationLimbal stem cell Deficiency; amniotic membrane transplantation
Limbal stem cell Deficiency; amniotic membrane transplantation
 
Laser in Ophthalmology
Laser in OphthalmologyLaser in Ophthalmology
Laser in Ophthalmology
 
Co Management Made Easier IOL
Co Management Made Easier IOL Co Management Made Easier IOL
Co Management Made Easier IOL
 
Introduction to phaco operation
Introduction to phaco operationIntroduction to phaco operation
Introduction to phaco operation
 
Qemu JIT Code Generator and System Emulation
Qemu JIT Code Generator and System EmulationQemu JIT Code Generator and System Emulation
Qemu JIT Code Generator and System Emulation
 
Postoperative Complication of DALK
Postoperative Complication of DALKPostoperative Complication of DALK
Postoperative Complication of DALK
 
Cómo presentar la tracción de tu negocio a posibles inversionistas
Cómo presentar la tracción de tu negocio a posibles inversionistasCómo presentar la tracción de tu negocio a posibles inversionistas
Cómo presentar la tracción de tu negocio a posibles inversionistas
 
AS OCT & UBM - Dr Shylesh B Dabke
AS OCT & UBM - Dr Shylesh B DabkeAS OCT & UBM - Dr Shylesh B Dabke
AS OCT & UBM - Dr Shylesh B Dabke
 
RSpec on Rails Tutorial
RSpec on Rails TutorialRSpec on Rails Tutorial
RSpec on Rails Tutorial
 
אוליבר
אוליבראוליבר
אוליבר
 
Patch Graft
Patch GraftPatch Graft
Patch Graft
 

En vedette

Shell,信号量以及java进程的退出
Shell,信号量以及java进程的退出Shell,信号量以及java进程的退出
Shell,信号量以及java进程的退出wang hongjiang
 
Scala-对Java的修正和超越
Scala-对Java的修正和超越Scala-对Java的修正和超越
Scala-对Java的修正和超越Caoyuan Deng
 
Java7 fork join framework and closures
Java7 fork join framework and closuresJava7 fork join framework and closures
Java7 fork join framework and closureswang hongjiang
 
中等创业公司后端技术选型
中等创业公司后端技术选型中等创业公司后端技术选型
中等创业公司后端技术选型wang hongjiang
 
深入剖析Concurrent hashmap中的同步机制(下)
深入剖析Concurrent hashmap中的同步机制(下)深入剖析Concurrent hashmap中的同步机制(下)
深入剖析Concurrent hashmap中的同步机制(下)wang hongjiang
 
Effective linux.2.(tools)
Effective linux.2.(tools)Effective linux.2.(tools)
Effective linux.2.(tools)wang hongjiang
 
Effective linux.3.(diagnosis)
Effective linux.3.(diagnosis)Effective linux.3.(diagnosis)
Effective linux.3.(diagnosis)wang hongjiang
 
深入剖析Concurrent hashmap中的同步机制(上)
深入剖析Concurrent hashmap中的同步机制(上)深入剖析Concurrent hashmap中的同步机制(上)
深入剖析Concurrent hashmap中的同步机制(上)wang hongjiang
 
Hash map导致cpu100% 的分析
Hash map导致cpu100% 的分析Hash map导致cpu100% 的分析
Hash map导致cpu100% 的分析wang hongjiang
 
Effective linux.1.(commandline)
Effective linux.1.(commandline)Effective linux.1.(commandline)
Effective linux.1.(commandline)wang hongjiang
 

En vedette (20)

Shell,信号量以及java进程的退出
Shell,信号量以及java进程的退出Shell,信号量以及java进程的退出
Shell,信号量以及java进程的退出
 
Scala-对Java的修正和超越
Scala-对Java的修正和超越Scala-对Java的修正和超越
Scala-对Java的修正和超越
 
Scala类型系统
Scala类型系统Scala类型系统
Scala类型系统
 
Enum开锁
Enum开锁Enum开锁
Enum开锁
 
Aswan&hump
Aswan&humpAswan&hump
Aswan&hump
 
聊一些电影
聊一些电影聊一些电影
聊一些电影
 
善用工具
善用工具善用工具
善用工具
 
Java7 fork join framework and closures
Java7 fork join framework and closuresJava7 fork join framework and closures
Java7 fork join framework and closures
 
Jvm内存管理基础
Jvm内存管理基础Jvm内存管理基础
Jvm内存管理基础
 
Ali-tomcat
Ali-tomcatAli-tomcat
Ali-tomcat
 
中等创业公司后端技术选型
中等创业公司后端技术选型中等创业公司后端技术选型
中等创业公司后端技术选型
 
functional-scala
functional-scalafunctional-scala
functional-scala
 
泛型总结
泛型总结泛型总结
泛型总结
 
Exodus2 大局观
Exodus2 大局观Exodus2 大局观
Exodus2 大局观
 
深入剖析Concurrent hashmap中的同步机制(下)
深入剖析Concurrent hashmap中的同步机制(下)深入剖析Concurrent hashmap中的同步机制(下)
深入剖析Concurrent hashmap中的同步机制(下)
 
Effective linux.2.(tools)
Effective linux.2.(tools)Effective linux.2.(tools)
Effective linux.2.(tools)
 
Effective linux.3.(diagnosis)
Effective linux.3.(diagnosis)Effective linux.3.(diagnosis)
Effective linux.3.(diagnosis)
 
深入剖析Concurrent hashmap中的同步机制(上)
深入剖析Concurrent hashmap中的同步机制(上)深入剖析Concurrent hashmap中的同步机制(上)
深入剖析Concurrent hashmap中的同步机制(上)
 
Hash map导致cpu100% 的分析
Hash map导致cpu100% 的分析Hash map导致cpu100% 的分析
Hash map导致cpu100% 的分析
 
Effective linux.1.(commandline)
Effective linux.1.(commandline)Effective linux.1.(commandline)
Effective linux.1.(commandline)
 

Similaire à Scala function-and-closures

Java Script 引擎技术
Java Script 引擎技术Java Script 引擎技术
Java Script 引擎技术bigqiang zou
 
潜力无限的编程语言Javascript
潜力无限的编程语言Javascript潜力无限的编程语言Javascript
潜力无限的编程语言Javascriptjay li
 
Clojure简介与应用
Clojure简介与应用Clojure简介与应用
Clojure简介与应用Robert Hao
 
Js的国(转载)
Js的国(转载)Js的国(转载)
Js的国(转载)Leo Hui
 
Ecmascript
EcmascriptEcmascript
Ecmascriptjay li
 
Ecma script edition5-小试
Ecma script edition5-小试Ecma script edition5-小试
Ecma script edition5-小试lydiafly
 
第六章 函數與巨集
第六章 函數與巨集第六章 函數與巨集
第六章 函數與巨集shademoon
 
ES5 introduction
ES5 introductionES5 introduction
ES5 introductionotakustay
 
Coding guideline
Coding guidelineCoding guideline
Coding guideline斯理 衛
 
所谓闭包
所谓闭包所谓闭包
所谓闭包youzitang
 
Java SE 8 的 Lambda 連鎖效應 - 語法、風格與程式庫
Java SE 8 的 Lambda 連鎖效應 - 語法、風格與程式庫Java SE 8 的 Lambda 連鎖效應 - 語法、風格與程式庫
Java SE 8 的 Lambda 連鎖效應 - 語法、風格與程式庫Justin Lin
 
揭开Javascript的面纱
揭开Javascript的面纱揭开Javascript的面纱
揭开Javascript的面纱qiang
 
Php extension开发
Php extension开发Php extension开发
Php extension开发thinkinlamp
 
所谓闭包
所谓闭包所谓闭包
所谓闭包ilovey4
 
認識 C++11 新標準及使用 AMP 函式庫作平行運算
認識 C++11 新標準及使用 AMP 函式庫作平行運算認識 C++11 新標準及使用 AMP 函式庫作平行運算
認識 C++11 新標準及使用 AMP 函式庫作平行運算建興 王
 
Programming python - part 1
Programming python - part 1Programming python - part 1
Programming python - part 1Che-Cheng Hsu
 
C++中级培训胶片
C++中级培训胶片C++中级培训胶片
C++中级培训胶片ff1
 

Similaire à Scala function-and-closures (20)

Java Script 引擎技术
Java Script 引擎技术Java Script 引擎技术
Java Script 引擎技术
 
潜力无限的编程语言Javascript
潜力无限的编程语言Javascript潜力无限的编程语言Javascript
潜力无限的编程语言Javascript
 
Clojure简介与应用
Clojure简介与应用Clojure简介与应用
Clojure简介与应用
 
Js的国(转载)
Js的国(转载)Js的国(转载)
Js的国(转载)
 
Ecmascript
EcmascriptEcmascript
Ecmascript
 
Ecma script edition5-小试
Ecma script edition5-小试Ecma script edition5-小试
Ecma script edition5-小试
 
第六章 函數與巨集
第六章 函數與巨集第六章 函數與巨集
第六章 函數與巨集
 
Dev307
Dev307Dev307
Dev307
 
ES5 introduction
ES5 introductionES5 introduction
ES5 introduction
 
Coding guideline
Coding guidelineCoding guideline
Coding guideline
 
所谓闭包
所谓闭包所谓闭包
所谓闭包
 
Sun java
Sun javaSun java
Sun java
 
Java SE 8 的 Lambda 連鎖效應 - 語法、風格與程式庫
Java SE 8 的 Lambda 連鎖效應 - 語法、風格與程式庫Java SE 8 的 Lambda 連鎖效應 - 語法、風格與程式庫
Java SE 8 的 Lambda 連鎖效應 - 語法、風格與程式庫
 
揭开Javascript的面纱
揭开Javascript的面纱揭开Javascript的面纱
揭开Javascript的面纱
 
Php extension开发
Php extension开发Php extension开发
Php extension开发
 
Eloquent ORM
Eloquent ORMEloquent ORM
Eloquent ORM
 
所谓闭包
所谓闭包所谓闭包
所谓闭包
 
認識 C++11 新標準及使用 AMP 函式庫作平行運算
認識 C++11 新標準及使用 AMP 函式庫作平行運算認識 C++11 新標準及使用 AMP 函式庫作平行運算
認識 C++11 新標準及使用 AMP 函式庫作平行運算
 
Programming python - part 1
Programming python - part 1Programming python - part 1
Programming python - part 1
 
C++中级培训胶片
C++中级培训胶片C++中级培训胶片
C++中级培训胶片
 

Scala function-and-closures

  • 2. 说明说明 ► 大纲大纲 1)1) 一等公民怎么体现一等公民怎么体现 2)2) 表达式表达式 3)3) 函数与方法函数与方法 4)4) 传值与传名传值与传名—— scalascala 中支持的参数传递方式中支持的参数传递方式 5)5) 高阶函数与柯里化高阶函数与柯里化 6)6) 偏应用函数偏应用函数 7)7) 偏函数偏函数 8)8) 一些谜题与细节一些谜题与细节 9)9) 闭包闭包 ► 交流:交流: http://www.atatech.org/scalahttp://www.atatech.org/scala 旺旺群:旺旺群: ScalaScala 交流和答疑交流和答疑 : 94329267,: 94329267, 密码:密码: sugsugsugsug ScalaScala 布道会:布道会: 554375392554375392 面向有经验者面向有经验者
  • 4. 函数作为一等公民体现在哪儿?函数作为一等公民体现在哪儿? 1)1) 可传递可传递 // 赋值赋值 2)2) 高阶高阶 3)3) 嵌套函数和匿名函数嵌套函数和匿名函数 4)4) 闭包闭包 5)5) 偏应用偏应用 (partial application)(partial application) 参考: http://en.wikipedia.org/wiki/First-class_function
  • 6. ScalaScala 中的表达式中的表达式 ► 1)1) 所有的表达式都有值所有的表达式都有值 ►2)2) 除了赋值和函数调用表达式,内置的几个除了赋值和函数调用表达式,内置的几个 表达式只有:表达式只有: if,while,for,try,matchif,while,for,try,match ►3)3) 块表达式块表达式 {{……}} 是基本表达式的组合,它的是基本表达式的组合,它的 值是最后一个表达式的值。值是最后一个表达式的值。
  • 7. ScalaScala 中的表达式中的表达式 ► 一些表达式的值:一些表达式的值: 1) a=1;1) a=1; 2) while(a<100){print(a)}2) while(a<100){print(a)} 3) if(a<2) 1;3) if(a<2) 1; ► 赋值表达式、赋值表达式、 whilewhile 表达式的值是表达式的值是 UnitUnit 类型类型 ,, 它的它的 值是唯一的值是唯一的 : (): () ► ifif 表达式缺乏表达式缺乏 elseelse 的话,不符合条件则返回的话,不符合条件则返回 UnitUnit 类类 型的型的 ()() ;即上面相当于:;即上面相当于: if(a<2) 1 else ()if(a<2) 1 else ()
  • 8. 赋值语句的注意点:赋值语句的注意点: 不同于不同于 javajava :: 11 )) while( (line = readLine() ) != null )while( (line = readLine() ) != null ) 不起作用,前边的不起作用,前边的 line = readLine()line = readLine() 得到的是得到的是 UnitUnit 类型值类型值 22 )) x=y=1; // y=1; x=()x=y=1; // y=1; x=() y=1y=1 表达式的结果是表达式的结果是 ()() ,, xx 被赋予了被赋予了 UnitUnit 类型类型 的值的值
  • 9. lambda: 函数字面量 (Function literal) (x :Int, y: Int) => x + y(x :Int, y: Int) => x + y 参数 函数体右箭头 产生一段匿名函数,类型为 (Int,Int)=>Int Scala 中参数的个数为 0 到 22 个。
  • 10. 函数函数 1)1) Function vs MethodFunction vs Method 2)2) Functor ?Functor ?
  • 11. Function,Method,FunctorFunction,Method,Functor 1)1) 狭义地区分狭义地区分 (( 从可传递性上从可传递性上 )) :: 方法方法 (method):(method): 指的是在指的是在 trait/class/objecttrait/class/object 中以中以 defdef 关键字声明的,它不能关键字声明的,它不能 被直接传递。被直接传递。 函数函数 (function):(function): 类型为类型为 ParamsType=>ResultTypeParamsType=>ResultType 的变量,这些变量背后的变量,这些变量背后 是用是用 FunctionNFunctionN 对象来封装的;可以被传递。方法可以转换为函数。对象来封装的;可以被传递。方法可以转换为函数。 2)2) 广义上,抛开背后的实现,方法就是函数;编译器某些场景自动把方法广义上,抛开背后的实现,方法就是函数;编译器某些场景自动把方法 封装为一个函数对象来传递。封装为一个函数对象来传递。 ScalaScala 社区并不特别区分这两个名词,注社区并不特别区分这两个名词,注 意语境,有时候函数就是指方法,有时候则是指函数对象意语境,有时候函数就是指方法,有时候则是指函数对象 3) Functor (3) Functor ( 函子函子 )) 是个高级概念,不可与是个高级概念,不可与 function,methodfunction,method 类比。类比。 暂简单理解为定义了暂简单理解为定义了 mapmap 方法的容器类型方法的容器类型
  • 12. 函数与方法函数与方法 ►定义方法:定义方法: def foo(i:Int) :Int = { i+2 }def foo(i:Int) :Int = { i+2 } 方法返回值类型不为方法返回值类型不为 UnitUnit 时,时, == 号不可省略号不可省略 def foo():Unit = {println(def foo():Unit = {println(““hihi””)})} 等同于等同于 def foo() {println(def foo() {println(““hihi””) }) } 返回类型为返回类型为 UnitUnit 时,时, == 号可以省略,返回值号可以省略,返回值 类型也可以省略。类型也可以省略。
  • 13. 函数与方法函数与方法 ► 函数函数 (( 值值 )) 的类型的类型 (( 入参类型入参类型 ) =>) => 出参类型出参类型 如同类与实例,函数类型是对函数的抽象。如同类与实例,函数类型是对函数的抽象。 每个通过每个通过 defdef 定义的方法本身并无类型之说,但被封装成的函数对象是定义的方法本身并无类型之说,但被封装成的函数对象是 有类型的,一些语境里说的方法类型也是指其函数类型有类型的,一些语境里说的方法类型也是指其函数类型 注意:注意: 定义方法时参个数可以多于定义方法时参个数可以多于 2222 个个 但函数只接受但函数只接受 00 到到 2222 个参数。个参数。 超过超过 2222 个参数的方法无法被封装为函数对象个参数的方法无法被封装为函数对象
  • 14. 函数与方法函数与方法 ► 函数函数 (( 值值 )) 的类型的类型 def foo() =def foo() = ““hihi”” 类型为类型为 : () => String //: () => String // 这里的这里的 ()() 表示空表示空 def foo() {println(def foo() {println(““hihi””)})} 类型为类型为 : () => Unit: () => Unit def foo(x:Int,y:Int) = x+ydef foo(x:Int,y:Int) = x+y 类型为类型为 : (Int,Int) => Int: (Int,Int) => Int
  • 15. 函数与方法函数与方法 ► 对函数变量赋值对函数变量赋值 :: 1)1) 对变量明确的声明函数类型对变量明确的声明函数类型 val f:Int=>Int = fooval f:Int=>Int = foo a) fooa) foo 是值是值 (( 函数对象函数对象 )) ,直接赋值。,直接赋值。 b) foob) foo 是方法,会被编译器封装为一个是方法,会被编译器封装为一个 Function1[Int,Int]Function1[Int,Int] 对象赋给对象赋给 ff 2)2) 通过字面量通过字面量 (lambda)(lambda) ,创建一个匿名函数赋值给变量,创建一个匿名函数赋值给变量 val f2 = (x:Int)=>x+1val f2 = (x:Int)=>x+1 编译器会推导出编译器会推导出 f2f2 的类型:的类型: Int=>IntInt=>Int 3)3) 部分应用部分应用 def foo(s:String) = {def foo(s:String) = {……}} val f3 = foo _val f3 = foo _
  • 16. 函数与方法函数与方法 在执行上:在执行上: 1)1) 方法的执行与方法的执行与 javajava 里的方法执行一致。里的方法执行一致。 2)2) 函数的执行,实际是对相应的函数的执行,实际是对相应的 FunctionFunction 对对 象调用其象调用其 applyapply 方法。方法。 3)3) 函数的执行效率要低于方法。函数的执行效率要低于方法。
  • 17. 函数与方法函数与方法 returnreturn 关键字:关键字: 1)1) 在方法中返回时可用可不用,但非最后一行返回必在方法中返回时可用可不用,但非最后一行返回必 须用须用 returnreturn 2)2) 通过字面量声明一个函数时,不能使用通过字面量声明一个函数时,不能使用 returnreturn val a = (x:Int)=>{return x+1} //errorval a = (x:Int)=>{return x+1} //error
  • 18. 函数与方法函数与方法 方法不能被直接传递方法不能被直接传递 ?? scala> def f1() = {println(1)}scala> def f1() = {println(1)} scala> def f2(f: ()=>Unit) = {f()}scala> def f2(f: ()=>Unit) = {f()} scala> f2(f1) //scala> f2(f1) // 这里不是直接传递方法么?这里不是直接传递方法么?
  • 19. tips:tips: 辅助工具辅助工具 scalac -Xshow-phasesscalac -Xshow-phases 列出所有的编译过程的各个象限,通常对我们有帮助列出所有的编译过程的各个象限,通常对我们有帮助 的是的是 typertyper 和和 jvmjvm 当拿不准一段代码最终被翻译为什么时,可通过当拿不准一段代码最终被翻译为什么时,可通过 scalacscalac ––Xprint:typerXprint:typer 或或 scalacscalac ––Xprint:jvmXprint:jvm 来看看代码被翻译成了什么来看看代码被翻译成了什么 也可以直接当脚本来调试,也可以直接当脚本来调试, egeg :: $ scala$ scala ––Xprint:typerXprint:typer ––ee ‘‘val a=2val a=2’’
  • 20. 函数与方法函数与方法 $ scala -Xprint:typer -e 'def f1()={println(1)}; def f2(f:()=>Unit){f1()}; f2(f1)$ scala -Xprint:typer -e 'def f1()={println(1)}; def f2(f:()=>Unit){f1()}; f2(f1)‘‘ ………… private def f1(): Unit = scala.this.Predef.println(111);private def f1(): Unit = scala.this.Predef.println(111); private def f2(f: () => Unit): Unit = $anon.this.f1();private def f2(f: () => Unit): Unit = $anon.this.f1(); $anon.this.f2({$anon.this.f2({ (() => $anon.this.f1())(() => $anon.this.f1()) })}) ………… 看到看到 f2(f1)f2(f1) 在调用的时候被转成了在调用的时候被转成了 f2( ()=>f1() )f2( ()=>f1() ) 参数参数 f1f1 被封装成了一个函数对象被封装成了一个函数对象
  • 21. 函数与方法函数与方法 无参函数无参函数 :: scala> def foo = {println(scala> def foo = {println(““hihi””)})} scala> fooscala> foo hihi 在定义和执行时都可以省略小括号。在定义和执行时都可以省略小括号。 统一访问原则统一访问原则 (uniform access principle)(uniform access principle) :: 客户代码不应由属性是通过字段实现还是方法实现而受影响。客户代码不应由属性是通过字段实现还是方法实现而受影响。 定义无参函数的惯例定义无参函数的惯例 :: 方法没有参数方法没有参数 &&&& 方法不改变可变状态方法不改变可变状态 (( 无副作用无副作用 ))
  • 22. 函数与方法函数与方法 无参函数:无参函数: 原则上,原则上, scalascala 中的函数都可以省略空括号,然而在中的函数都可以省略空括号,然而在 调用的方法超出其调用者对象的属性时,推荐仍保调用的方法超出其调用者对象的属性时,推荐仍保 持空括号。持空括号。 比如,方法执行了比如,方法执行了 I/O,I/O, 或有改变可变状态,或读取了或有改变可变状态,或读取了 不是调用者字段的不是调用者字段的 varvar ,总之无论是直接还是非直,总之无论是直接还是非直 接的使用可变对象都应该加空括号接的使用可变对象都应该加空括号 "hello".length //"hello".length // 没有副作用,可以省略括号没有副作用,可以省略括号 println() //println() // 最好别省略最好别省略 ()()
  • 23. 函数与方法函数与方法 返回值为函数的函数:返回值为函数的函数: scala> def hf = (x:Int) => x+1scala> def hf = (x:Int) => x+1 hf: Int => Inthf: Int => Int scala> hfscala> hf //// 执行结果是一个函数对象执行结果是一个函数对象 res0: Int => Int = <function1>res0: Int => Int = <function1> scala> hf(2)scala> hf(2) res2: Int = 3res2: Int = 3 相当于两次调用,第一次先得到一个函数对象,然后在对这个函数对象传相当于两次调用,第一次先得到一个函数对象,然后在对这个函数对象传 参调用参调用
  • 25. 有名参数有名参数 (named argments)(named argments) 提高可读性提高可读性 scala> def printName(first:String, last:String) {scala> def printName(first:String, last:String) { println(first + " " + last)println(first + " " + last) }} scala> printName(last="wang", first="hongjiang")scala> printName(last="wang", first="hongjiang") hongjiang wanghongjiang wang
  • 26. 缺省参数缺省参数 (default arguments)(default arguments) ► scala> def greeting(name:String, msg:String = "hi”){scala> def greeting(name:String, msg:String = "hi”){ println(msg+","+name)println(msg+","+name) }} ► scala> greeting("hongjiang")scala> greeting("hongjiang") hi,hongjianghi,hongjiang
  • 27. 缺省参数缺省参数 (default arguments)(default arguments) ►缺省参数的一个适用场景:提供缺省参数的一个适用场景:提供 copycopy 方法时方法时 scala> class A(val a:String,val b:String) {scala> class A(val a:String,val b:String) { def copy(a:String="AAA",b:String="BBB") = new A(a,b)def copy(a:String="AAA",b:String="BBB") = new A(a,b) }} scala> val a = new A("a","b")scala> val a = new A("a","b") scala> val b = a.copy()scala> val b = a.copy() scala> b.ascala> b.a res3: String = AAAres3: String = AAA case classcase class 里的里的 copycopy 方法是由编译器生成的,使用了缺省参数。方法是由编译器生成的,使用了缺省参数。
  • 28. 参数的传递参数的传递 传值 ? 传址? 还是?传值 ? 传址? 还是?
  • 29. CTMCTM 中定义的中定义的 66 种传值方式种传值方式 11 Call by referenceCall by reference 22 Call by varible //1Call by varible //1 的特例的特例 33 Call by value-result //2Call by value-result //2 的变种的变种 44 Call by valueCall by value //Java//Java 中只支持这一种中只支持这一种 55 Call by nameCall by name //Scala//Scala 中支持中支持 66 Call by needCall by need //5//5 的变种的变种 注:注: 1)1) 引用自:引用自: http://blog.csdn.net/sunqihui/article/details/5597995http://blog.csdn.net/sunqihui/article/details/5597995 2) CTM:2) CTM: 《计算机编程的概念、技术与模型》《计算机编程的概念、技术与模型》 http://book.douban.com/subject/1782316/http://book.douban.com/subject/1782316/
  • 30. 传值还是传名传值还是传名 对比两段函数:对比两段函数: 片段片段 1:1: scala> def foo(f : ()=>String) {scala> def foo(f : ()=>String) { println("111");println("111"); println(f()+println(f()+““okok””) //) // 写为写为 ff 会如何?会如何? }} scala> foo( {println("AAA"); ()=>"AAA"} )scala> foo( {println("AAA"); ()=>"AAA"} ) 传值?传名?结果是什么?传值?传名?结果是什么?
  • 31. 传值还是传名传值还是传名 片段片段 22 :: scala> def bar(f : =>String) { //scala> def bar(f : =>String) { // 省略了小括号省略了小括号 println("111");println("111"); println(f +println(f + ““okok””) //f) //f 而不是而不是 f()f() }} scala> bar( {println("AAA"); "AAA"} )scala> bar( {println("AAA"); "AAA"} ) 传值?传名?结果是什么?传值?传名?结果是什么?
  • 32. 传值还是传名传值还是传名 foo( {println("AAA"); ()=>"AAA"} )foo( {println("AAA"); ()=>"AAA"} ) 传递的是表达式的结果,表达式被执行 / 求值 bar( {println("AAA"); "AAA"} )bar( {println("AAA"); "AAA"} ) 传递的是表达式,而非结果,这里不被执行
  • 33. 传值还是传名传值还是传名 ►1) by name1) by name 传递只出现在函数参数中传递只出现在函数参数中 ►2)2) 同上,同上, =>R=>R 类型只能出现在函数参数中类型只能出现在函数参数中 表示表示 by name parameterby name parameter ►3) => R3) => R 不能单纯看作是不能单纯看作是 ()=>R()=>R 的缩写,两的缩写,两 者传递形式不同者传递形式不同
  • 35. 高阶函数高阶函数 ► higher-order function:higher-order function: 以其他函数做参数的函数。以其他函数做参数的函数。 eg:eg: scala> def f2(f: ()=>Unit) { f() }scala> def f2(f: ()=>Unit) { f() } f2: (f: () => Unit)Unitf2: (f: () => Unit)Unit scala> def f1() {println(1)}scala> def f1() {println(1)} f1: ()Unitf1: ()Unit scala> f2(f1)scala> f2(f1) 11 scala> f2(()=>println(scala> f2(()=>println(““hihi””)) //)) // 传入匿名函数传入匿名函数 hihi
  • 36. 高阶函数高阶函数 ► 一些集合中的高阶函数的例子:一些集合中的高阶函数的例子: scala> val a = Array(1,2,3)scala> val a = Array(1,2,3) a: Array[Int] = Array(1, 2, 3)a: Array[Int] = Array(1, 2, 3) scala> a.map(_ + 1) // x=>x+1scala> a.map(_ + 1) // x=>x+1 res3: Array[Int] = Array(2, 3, 4)res3: Array[Int] = Array(2, 3, 4) scala> a.filter(_ > 2) // x=>x>2scala> a.filter(_ > 2) // x=>x>2 res9: Array[Int] = Array(3)res9: Array[Int] = Array(3)
  • 38. 柯里化柯里化 (currying)(currying) scala> def sum(x:Int, y:Int) = x+y sum: (x: Int, y: Int)Int // 参数打散,两个参数分开 scala> def sum2(x:Int)(y:Int) = x+y sum2: (x: Int)(y: Int)Int
  • 39. 柯里化柯里化 (currying)(currying) scala> sum2(1)(2) res1: Int = 3 // 上面的调用相当于下面的几个步骤 scala> def first(x:Int) = (y:Int)=>x+y first: (x: Int)Int => Int scala> first(1) res2: Int => Int = <function1> scala> val second = first(1) second: Int => Int = <function1> scala> second(2) res3: Int = 3
  • 40. 柯里化柯里化 (currying)(currying) ► 函数链函数链 把一个带有多个参数的函数,转换为多个把一个带有多个参数的函数,转换为多个 只有一个参数的函数来执行只有一个参数的函数来执行 f(1)(2)(3)f(1)(2)(3)  ((((f(1)f(1)))(2)(2)))(3)(3) fa(1) fb(2) fc(3) 产生新的函数 带入参数 1 执行 产生新的函数 x 得到最终的值 带入参数 2 执行 带入参数 3 执行
  • 41. 柯里化柯里化 (currying)(currying) ► 柯理化的意义?柯理化的意义? 控制抽象,可改变代码的书写风格。控制抽象,可改变代码的书写风格。 1) foo(res, ()=>print(1) foo(res, ()=>print(““test))test)) 2) foo(res)(()=>print(2) foo(res)(()=>print(““testtest””)))) 3) foo(res){3) foo(res){ ()=>print(()=>print(““testtest””)) }}
  • 42. 柯里化柯里化 (currying)(currying) ►模拟模拟 C#C# 里的里的 usingusing 自动释放资源的写法:自动释放资源的写法: using(res) {using(res) { handle(handle(……)) }} resres 会被自动释放会被自动释放 resres 必须是必须是 IDisposeableIDisposeable 接口子类接口子类
  • 43. 柯里化柯里化 (currying)(currying) ►模拟模拟 C#C# 里的里的 usingusing ScalaScala 里的实现里的实现 //// 暂忽略类型表达暂忽略类型表达 def using[C <% { def close(): Unit }, T](resource: C)(handle: C => T): T = {def using[C <% { def close(): Unit }, T](resource: C)(handle: C => T): T = { try {try { handle(resource)handle(resource) } finally {} finally { closeQuietly(resource)closeQuietly(resource) }} }}
  • 44. 柯里化柯里化 (currying)(currying) ►模拟模拟 C#C# 里的里的 usingusing //// 使用使用 usingusing using(Source.fromURL(cl.getResource(file))) {using(Source.fromURL(cl.getResource(file))) { stream => {stream => { for (line <- stream.getLines){for (line <- stream.getLines){……}} }} resultresult }}
  • 45. 偏应用函数偏应用函数 (partial application function)(partial application function) 也被翻译部分应用函数也被翻译部分应用函数 把一个函数适配为另一个函数把一个函数适配为另一个函数
  • 46. 偏应用函数偏应用函数 (partial application function)(partial application function) 占位符:占位符: __ scala> def pow(x:Int, y:Int) = Math.pow(x,y)scala> def pow(x:Int, y:Int) = Math.pow(x,y) pow: (x: Int, y: Int)Doublepow: (x: Int, y: Int)Double scala> pow(2,3)scala> pow(2,3) res4: Double = 8.0res4: Double = 8.0 scala> val square = pow(_:Int, 2)scala> val square = pow(_:Int, 2) square: Int => Double = <function1>square: Int => Double = <function1> scala> square(3)scala> square(3) res5: Double = 9.0res5: Double = 9.0
  • 47. 偏应用函数偏应用函数 (partial application function)(partial application function) scala> def log(time:Date, msg:String) { println(time + ": " + msg) }scala> def log(time:Date, msg:String) { println(time + ": " + msg) } log: (time: java.util.Date, msg: String)Unitlog: (time: java.util.Date, msg: String)Unit scala> val log2 = log(new Date, _:String)scala> val log2 = log(new Date, _:String) log2: String => Unit = <function1>log2: String => Unit = <function1> scala> log2("test1")scala> log2("test1") scala> log2("test2")scala> log2("test2") scala> log2("test3")scala> log2("test3") 三次时间一样吗?三次时间一样吗? 绑定的是表达式,还是表达式的结果?绑定的是表达式,还是表达式的结果?
  • 48. 偏应用函数偏应用函数 (partial application function)(partial application function) 不绑定任何参数不绑定任何参数 scala> val pow2 = pow _scala> val pow2 = pow _ pow2: (Int, Int) => Double = <function2>pow2: (Int, Int) => Double = <function2> 注意:一些书和资料里对接收函数类型参数的地方,传递时需注意:一些书和资料里对接收函数类型参数的地方,传递时需 要显式的把方法转换为函数对象要显式的把方法转换为函数对象 :: 通过通过 __ 可以快速实现。可以快速实现。 那可能是较低版本的编译器。那可能是较低版本的编译器。 最近版本的编译器已不需要,发现是方法会自动封装成一个函最近版本的编译器已不需要,发现是方法会自动封装成一个函 数。数。
  • 49. 偏函数偏函数 (partial function)(partial function) 注意和偏应用函数是两回事
  • 50. 偏函数偏函数 (partial function)(partial function) 图片来源图片来源 : http://developer.51cto.com/art/200912/166875.htm: http://developer.51cto.com/art/200912/166875.htm 也被翻译为也被翻译为““部分函数部分函数””,区分于,区分于““完全函数完全函数”” 从数学上说,偏函数就是只实现了部分映射的函数:从数学上说,偏函数就是只实现了部分映射的函数:
  • 51. 偏函数偏函数 (( 列子列子 )) 1)1) 草原很好的解释了草原很好的解释了““偏函数偏函数””的概念以及用途:的概念以及用途: https://groups.google.com/forum/?fromgroups=#!https://groups.google.com/forum/?fromgroups=#! topic/scalacn/ASo80yip9fAtopic/scalacn/ASo80yip9fA 2)2) 必须声明为必须声明为 PartialFunctionPartialFunction ;主要通过模式匹配;主要通过模式匹配 来实现来实现
  • 52. 偏函数的组合偏函数的组合 通过通过 andThenandThen 或或 orElseorElse 来组合偏函数:来组合偏函数: scala> def p1:PartialFunction[String,String] = {scala> def p1:PartialFunction[String,String] = { case "A" => "OK"case "A" => "OK" }} scala> def p3:PartialFunction[String,String] = {scala> def p3:PartialFunction[String,String] = { case "OK" => "haha"case "OK" => "haha" }} scala> (p1 andThen p3)("A")   //  A => OK => hahascala> (p1 andThen p3)("A")   //  A => OK => haha res3: String = hahares3: String = haha
  • 54. 一些谜题一些谜题 定义方法时省略小括号定义方法时省略小括号 :: scala> def m = "hi"scala> def m = "hi" m: String //m: String // 为什么不是为什么不是 ()String()String scala> val f:()=>String = m //scala> val f:()=>String = m // 会如何?会如何? scala> def m() = "hi”scala> def m() = "hi” scala> val f:()=>String = m //scala> val f:()=>String = m // 又会如何?又会如何? 怎么理解?怎么理解?
  • 55. 一些谜题一些谜题 定义方法时省略小括号定义方法时省略小括号 :: scala> class MyClass { def apply() { println("my class") } }scala> class MyClass { def apply() { println("my class") } } scala> def foo = new MyClassscala> def foo = new MyClass scala> fooscala> foo scala> foo() //scala> foo() // 结果是什么?结果是什么? // foo// foo 与与 foo()foo() 的差异?的差异?
  • 56. 一些谜题一些谜题 ►scalascala 里里  def foo() = xxxdef foo() = xxx 在调用时可以省略在调用时可以省略 ff 后边的后边的 ()()  但定义时如果不带小括号但定义时如果不带小括号 def foo = xxxdef foo = xxx 则调用则调用 时加时加 ()() 要注意,要注意, foo()foo() 被翻译为了被翻译为了 (foo).apply()(foo).apply()
  • 57. 一些谜题一些谜题 ►UnitUnit 的问题:的问题: 1)1) 为何不用为何不用 javajava 的的 VoidVoid 类型,而引入类型,而引入 UnitUnit 类类 型?型? a)a) 类型一致性类型一致性 b) voidb) void 是面向函数的,是面向函数的, unitunit 除了可以是函数返回类型也可以除了可以是函数返回类型也可以 是变量的类型。另,每个表达式是变量的类型。另,每个表达式 // 语句都有值,一些表达式语句都有值,一些表达式 的值为的值为 unitunit
  • 58. 一些谜题一些谜题 Unit 的问题: val a = () => Unit // aval a = () => Unit // a 是什么类型?是什么类型? val b = () => {} //val b = () => {} // 有什么不同?有什么不同? 注意:第一个匿名函数中的注意:第一个匿名函数中的 UnitUnit 是伴生对象是伴生对象
  • 59. 一些谜题一些谜题 UnitUnit 的问题:的问题: 1)1)def foo(f: Unit)def foo(f: Unit) 2)2)def foo(f: =>Unit)def foo(f: =>Unit) 3)3)def foo(f: ()=>Unit)def foo(f: ()=>Unit) 不同在哪儿?不同在哪儿?
  • 60. 一些谜题一些谜题 UnitUnit 的问题:的问题: 1)1)def foo(f: Unit)def foo(f: Unit) 2)2)def foo(f: =>Unit)def foo(f: =>Unit) 对上面的方法传入对上面的方法传入 foo(2), foo(2,3,”4”) ?foo(2), foo(2,3,”4”) ? 关于使用关于使用 unitunit 做参数的讨论:做参数的讨论: http://www.atatech.org/qa/detail/13423?group_id=51http://www.atatech.org/qa/detail/13423?group_id=51
  • 63. 闭包的本质:代码块闭包的本质:代码块 ++ 上下文上下文 关于引用环境的绑定关于引用环境的绑定 (The Binding of(The Binding of Referencing Environments)Referencing Environments) ,, 先通过一个先通过一个 javajava 的匿名内部类来看:的匿名内部类来看:
  • 64. JavaJava 中的匿名内部类如何访问局部变量中的匿名内部类如何访问局部变量 public Thread createThread(){public Thread createThread(){ //// 提升局部变量的生命周期提升局部变量的生命周期 finalfinal int innerVar = 100;int innerVar = 100; return new Thread(){return new Thread(){ public void run(){public void run(){ System.out.println(innerVar);System.out.println(innerVar); }} };}; }} innerVarinnerVar 还是分配在栈空间上么?还是分配在栈空间上么? JavaJava 的匿名内部类,和闭包很像。但用匿名内部类来实现,前的匿名内部类,和闭包很像。但用匿名内部类来实现,前 提是先要定义好该行为的接口。繁琐一些,不那么灵活提是先要定义好该行为的接口。繁琐一些,不那么灵活
  • 66. 一个一个 javascriptjavascript 的例子的例子 var div = document.getElementById("testDiv");var div = document.getElementById("testDiv"); var events = {onclick: "clicked",var events = {onclick: "clicked", onchange: "changed",onchange: "changed", onmouseover: "mouse over"};onmouseover: "mouse over"}; for(for(ee in events){in events){ div[e] = function(){div[e] = function(){ alert(events[e]);alert(events[e]); };}; }}
  • 67. 解决方式:多一层抽象解决方式:多一层抽象 for(e in events){for(e in events){ div[e] = function(div[e] = function(e){){ return function(){return function(){ alert(events[e]);alert(events[e]); };}; }(e);}(e); }} 每次绑定不同的局部对象。每次绑定不同的局部对象。 多加的这层函数叫做因子函数多加的这层函数叫做因子函数 (factor function)(factor function) rubyruby 的见:的见: http://www.javaeye.com/topic/156337http://www.javaeye.com/topic/156337
  • 68. 闭包:更深入的了解闭包:更深入的了解 The Binding of Referencing EnvironmentsThe Binding of Referencing Environments (( 引用环境的约束引用环境的约束 )) 在递归的情况会是怎样的?在递归的情况会是怎样的?
  • 69. 闭包的早绑定和晚绑定闭包的早绑定和晚绑定 (( 深约束深约束 // 浅约浅约 束束 )) program binding_example(input, output);program binding_example(input, output); procedure A(I : integer; procedure P);procedure A(I : integer; procedure P); procedure B; //procedure B; // 子函数子函数 BB beginbegin writeln(I);writeln(I); end;end; begin (* A *)begin (* A *) if I > 1 thenif I > 1 then PP elseelse A(2,B); //A(2,B); // 递归递归 end;end; procedure C; begin end;procedure C; begin end; begin (* main *)begin (* main *) A(1, C);A(1, C); endend
  • 70. PascalPascal 里的深约束。在通过形式参数里的深约束。在通过形式参数 PP 调调 用用 BB 时,存在着时,存在着 II 的两个实例。由于的两个实例。由于 PP 的的 闭包是在闭包是在 AA 的第一个调用中创建的,因此的第一个调用中创建的,因此 它使用该调用时的那个它使用该调用时的那个 II ,因此打印出,因此打印出 11 。。 ------------------------ 以上摘自《程序设计语言以上摘自《程序设计语言————实践之路》第二版,引用环境的约束一节。实践之路》第二版,引用环境的约束一节。 下面是一个同事把上面的代码翻译为下面是一个同事把上面的代码翻译为 C#C# ,看看是不是一样,他的回复:,看看是不是一样,他的回复:
  • 71. ► 确实确实 C#C# 也是这样。不过用也是这样。不过用 C#C# 的语法写出来的代码,看上去结果比的语法写出来的代码,看上去结果比 PascalPascal 要明显一些,要明显一些, 我觉得。我觉得。               static void A(int i, Action p)static void A(int i, Action p)               {{                      if (i > 1)if (i > 1)                             p();p();                      elseelse                             A(2, () => Console.WriteLine(i));A(2, () => Console.WriteLine(i));               }} ► C#C# 不允许函数嵌套,只允许函数嵌套不允许函数嵌套,只允许函数嵌套 closureclosure ,当然也可以写成:,当然也可以写成:               static void B(int i)static void B(int i)               {{                      Console.WriteLine(i);Console.WriteLine(i);               }}               static void A(int i, Action p)static void A(int i, Action p)               {{                      if (i > 1)if (i > 1)                             p();p();                      elseelse                             A(2, () => B(i));A(2, () => B(i));               }}
  • 72. ► 结果也没有差别,其实前面那种写法结果也没有差别,其实前面那种写法 Console.WriteLineConsole.WriteLine 就是就是 BB 。。 这两种写法看上去结果都是很明显的,调用这两种写法看上去结果都是很明显的,调用 WriteLineWriteLine 的那个的那个 ii 只能是只能是 11 。。 PascalPascal 的的 closureclosure 依赖的是帧(一个函数调用发生时的栈信息)指针的依赖的是帧(一个函数调用发生时的栈信息)指针的 传递,所有变量都还是存在于栈上;而传递,所有变量都还是存在于栈上;而 C#C# 是靠把是靠把 closureclosure 中用到的变量中用到的变量 包装进一个(匿名)类,包装进一个(匿名)类, closureclosure 本身则是该类的一个方法。本身则是该类的一个方法。 PascalPascal 的基于帧的实现是所有试图实现的基于帧的实现是所有试图实现 closureclosure 而又没有自动垃圾回收而又没有自动垃圾回收 机制的语言的无奈之举,这种方法不仅看上去比较费解,而且在应用上机制的语言的无奈之举,这种方法不仅看上去比较费解,而且在应用上 也有限制--用帧实现的也有限制--用帧实现的 closureclosure 难以在多线程环境中使用,因为难以在多线程环境中使用,因为 closureclosure 中的变量存在于栈上,中的变量存在于栈上, closureclosure 能否得到执行完全取决于构造能否得到执行完全取决于构造 closureclosure 的那个函数是否已经返回,也就是说,构造的那个函数是否已经返回,也就是说,构造 closureclosure 的函数必须的函数必须 等待,知道等待,知道 closureclosure 执行完毕才能返回。执行完毕才能返回。 比如比如 C#C# 中中 closureclosure 经常被用于执行一些异步调用,如果是基于帧的经常被用于执行一些异步调用,如果是基于帧的 closureclosure 在这些方面就很难得到有效应用了。在这些方面就很难得到有效应用了。
  • 73. 闭包的早绑定和晚绑定闭包的早绑定和晚绑定 ►ScalaScala 里的实现:里的实现: scala> def a(i:Int, f: =>Unit) {scala> def a(i:Int, f: =>Unit) { def b() {println(i)} //def b() {println(i)} // 嵌套函数嵌套函数 if (i>1) f else a(2,b) //if (i>1) f else a(2,b) // 递归递归 }} scala> a(1, {})scala> a(1, {})
  • 74. 参考参考 http://delicious.com/w.hongjiang/closureshttp://delicious.com/w.hongjiang/closures http://james-iry.blogspot.comhttp://james-iry.blogspot.com http://twitter.github.io/effectivescala/http://twitter.github.io/effectivescala/ http://news.cnblogs.com/n/175549/http://news.cnblogs.com/n/175549/ http://www.yinwang.org/blog-cn/2013/04/02/currying/ 《程序设计语言《程序设计语言————实践之路》第二版实践之路》第二版 Follow me:Follow me: http://weibo.com/woodcafehttp://weibo.com/woodcafe https://twitter.com/woodcafehttps://twitter.com/woodcafe http://www.laiwang.com/u/3001http://www.laiwang.com/u/3001 http://www.slideshare.net/hongjiang //ppthttp://www.slideshare.net/hongjiang //ppt 和和 pdfpdf http://hongjiang.info (http://hongjiang.info ( 内容后续放入内容后续放入 ))