SlideShare a Scribd company logo
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 ( 内容后续放入内容后续放入 ))

More Related Content

PDF
el-canario-polaco-pdf
PDF
Alter ego 01
PDF
PDF
Della giacoma cavalleria-rusticana-clarinet-and-piano
PDF
Grammarway 3
PDF
Bl S-G - KM - ZS 420
PDF
Tech Mahindra Certificate
PDF
854 konacni obracun
el-canario-polaco-pdf
Alter ego 01
Della giacoma cavalleria-rusticana-clarinet-and-piano
Grammarway 3
Bl S-G - KM - ZS 420
Tech Mahindra Certificate
854 konacni obracun

What's hot (13)

PDF
3.1 enfoque teoricos de las ciencias sociales. prinipales corri
PDF
Quran chapter-42-surah-ash-shura-pdf
PDF
Click on 2 student's book
PDF
Dok holidej 424 frenk larami - grad ucenjenih (drzeko &amp; folpi &amp; eme...
PDF
Impacto de la ciencia y la tecnologia dgo
PDF
Curso de idiomas globo inglês livro006
PDF
The Walking Dead - Comic No 5
PDF
Libro derecho-civil-i-carlos-vasquez-ortiz
PDF
Sashary Rios - Goethe-Zertifikat B1
PDF
Click on 2 workbook
PDF
07 desarrollo de la lengua oral y escrita en el preescolar ant básica
PPTX
Unity Shader教學:Unity ShaderLab簡介 part2
3.1 enfoque teoricos de las ciencias sociales. prinipales corri
Quran chapter-42-surah-ash-shura-pdf
Click on 2 student's book
Dok holidej 424 frenk larami - grad ucenjenih (drzeko &amp; folpi &amp; eme...
Impacto de la ciencia y la tecnologia dgo
Curso de idiomas globo inglês livro006
The Walking Dead - Comic No 5
Libro derecho-civil-i-carlos-vasquez-ortiz
Sashary Rios - Goethe-Zertifikat B1
Click on 2 workbook
07 desarrollo de la lengua oral y escrita en el preescolar ant básica
Unity Shader教學:Unity ShaderLab簡介 part2
Ad

Viewers also liked (20)

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

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 ( 内容后续放入内容后续放入 ))