Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Scala function-and-closures

3,057 views

Published on

  • 嘗試使用我們的服務。我們已經在市場上獲得他們的名字,向你保證我們的工作質量。中国论文网
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here

Scala function-and-closures

  1. 1. ScalaScala 中的函数与闭包中的函数与闭包hongjiang 2013.4.23hongjiang 2013.4.23
  2. 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, 密码:密码: sugsugsugsugScalaScala 布道会:布道会: 554375392554375392 面向有经验者面向有经验者
  3. 3. 函数作为一等公民体现在哪儿?函数作为一等公民体现在哪儿?first-class citizenfirst-class citizen
  4. 4. 函数作为一等公民体现在哪儿?函数作为一等公民体现在哪儿?1)1) 可传递可传递 // 赋值赋值2)2) 高阶高阶3)3) 嵌套函数和匿名函数嵌套函数和匿名函数4)4) 闭包闭包5)5) 偏应用偏应用 (partial application)(partial application)参考: http://en.wikipedia.org/wiki/First-class_function
  5. 5. ScalaScala 中的表达式中的表达式
  6. 6. ScalaScala 中的表达式中的表达式► 1)1) 所有的表达式都有值所有的表达式都有值►2)2) 除了赋值和函数调用表达式,内置的几个除了赋值和函数调用表达式,内置的几个表达式只有:表达式只有: if,while,for,try,matchif,while,for,try,match►3)3) 块表达式块表达式 {{……}} 是基本表达式的组合,它的是基本表达式的组合,它的值是最后一个表达式的值。值是最后一个表达式的值。
  7. 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. 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. 9. lambda: 函数字面量 (Functionliteral)(x :Int, y: Int) => x + y(x :Int, y: Int) => x + y参数 函数体右箭头产生一段匿名函数,类型为 (Int,Int)=>IntScala 中参数的个数为 0 到 22 个。
  10. 10. 函数函数1)1) Function vs MethodFunction vs Method2)2) Functor ?Functor ?
  11. 11. Function,Method,FunctorFunction,Method,Functor1)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. 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. 13. 函数与方法函数与方法► 函数函数 (( 值值 )) 的类型的类型(( 入参类型入参类型 ) =>) => 出参类型出参类型如同类与实例,函数类型是对函数的抽象。如同类与实例,函数类型是对函数的抽象。每个通过每个通过 defdef 定义的方法本身并无类型之说,但被封装成的函数对象是定义的方法本身并无类型之说,但被封装成的函数对象是有类型的,一些语境里说的方法类型也是指其函数类型有类型的,一些语境里说的方法类型也是指其函数类型注意:注意:定义方法时参个数可以多于定义方法时参个数可以多于 2222 个个但函数只接受但函数只接受 00 到到 2222 个参数。个参数。超过超过 2222 个参数的方法无法被封装为函数对象个参数的方法无法被封装为函数对象
  14. 14. 函数与方法函数与方法► 函数函数 (( 值值 )) 的类型的类型def foo() =def foo() = ““hihi””类型为类型为 : () => String //: () => String // 这里的这里的 ()() 表示空表示空def foo() {println(def foo() {println(““hihi””)})}类型为类型为 : () => Unit: () => Unitdef foo(x:Int,y:Int) = x+ydef foo(x:Int,y:Int) = x+y类型为类型为 : (Int,Int) => Int: (Int,Int) => Int
  15. 15. 函数与方法函数与方法► 对函数变量赋值对函数变量赋值 ::1)1) 对变量明确的声明函数类型对变量明确的声明函数类型val f:Int=>Int = fooval f:Int=>Int = fooa) fooa) foo 是值是值 (( 函数对象函数对象 )) ,直接赋值。,直接赋值。b) foob) foo 是方法,会被编译器封装为一个是方法,会被编译器封装为一个 Function1[Int,Int]Function1[Int,Int] 对象赋给对象赋给 ff2)2) 通过字面量通过字面量 (lambda)(lambda) ,创建一个匿名函数赋值给变量,创建一个匿名函数赋值给变量val f2 = (x:Int)=>x+1val f2 = (x:Int)=>x+1编译器会推导出编译器会推导出 f2f2 的类型:的类型: Int=>IntInt=>Int3)3) 部分应用部分应用def foo(s:String) = {def foo(s:String) = {……}}val f3 = foo _val f3 = foo _
  16. 16. 函数与方法函数与方法在执行上:在执行上:1)1) 方法的执行与方法的执行与 javajava 里的方法执行一致。里的方法执行一致。2)2) 函数的执行,实际是对相应的函数的执行,实际是对相应的 FunctionFunction 对对象调用其象调用其 applyapply 方法。方法。3)3) 函数的执行效率要低于方法。函数的执行效率要低于方法。
  17. 17. 函数与方法函数与方法returnreturn 关键字:关键字:1)1) 在方法中返回时可用可不用,但非最后一行返回必在方法中返回时可用可不用,但非最后一行返回必须用须用 returnreturn2)2) 通过字面量声明一个函数时,不能使用通过字面量声明一个函数时,不能使用 returnreturnval a = (x:Int)=>{return x+1} //errorval a = (x:Int)=>{return x+1} //error
  18. 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. 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. 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. 21. 函数与方法函数与方法无参函数无参函数 ::scala> def foo = {println(scala> def foo = {println(““hihi””)})}scala> fooscala> foohihi在定义和执行时都可以省略小括号。在定义和执行时都可以省略小括号。统一访问原则统一访问原则 (uniform access principle)(uniform access principle) ::客户代码不应由属性是通过字段实现还是方法实现而受影响。客户代码不应由属性是通过字段实现还是方法实现而受影响。定义无参函数的惯例定义无参函数的惯例 ::方法没有参数方法没有参数 &&&& 方法不改变可变状态方法不改变可变状态 (( 无副作用无副作用 ))
  22. 22. 函数与方法函数与方法无参函数:无参函数:原则上,原则上, scalascala 中的函数都可以省略空括号,然而在中的函数都可以省略空括号,然而在调用的方法超出其调用者对象的属性时,推荐仍保调用的方法超出其调用者对象的属性时,推荐仍保持空括号。持空括号。比如,方法执行了比如,方法执行了 I/O,I/O, 或有改变可变状态,或读取了或有改变可变状态,或读取了不是调用者字段的不是调用者字段的 varvar ,总之无论是直接还是非直,总之无论是直接还是非直接的使用可变对象都应该加空括号接的使用可变对象都应该加空括号"hello".length //"hello".length // 没有副作用,可以省略括号没有副作用,可以省略括号println() //println() // 最好别省略最好别省略 ()()
  23. 23. 函数与方法函数与方法返回值为函数的函数:返回值为函数的函数:scala> def hf = (x:Int) => x+1scala> def hf = (x:Int) => x+1hf: Int => Inthf: Int => Intscala> hfscala> hf //// 执行结果是一个函数对象执行结果是一个函数对象res0: Int => Int = <function1>res0: Int => Int = <function1>scala> hf(2)scala> hf(2)res2: Int = 3res2: Int = 3相当于两次调用,第一次先得到一个函数对象,然后在对这个函数对象传相当于两次调用,第一次先得到一个函数对象,然后在对这个函数对象传参调用参调用
  24. 24. 有名参数与缺省参数有名参数与缺省参数
  25. 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. 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. 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.ares3: String = AAAres3: String = AAAcase classcase class 里的里的 copycopy 方法是由编译器生成的,使用了缺省参数。方法是由编译器生成的,使用了缺省参数。
  28. 28. 参数的传递参数的传递传值 ? 传址? 还是?传值 ? 传址? 还是?
  29. 29. CTMCTM 中定义的中定义的 66 种传值方式种传值方式11 Call by referenceCall by reference22 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/55979952) CTM:2) CTM: 《计算机编程的概念、技术与模型》《计算机编程的概念、技术与模型》 http://book.douban.com/subject/1782316/http://book.douban.com/subject/1782316/
  30. 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. 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. 32. 传值还是传名传值还是传名foo( {println("AAA"); ()=>"AAA"} )foo( {println("AAA"); ()=>"AAA"} )传递的是表达式的结果,表达式被执行 / 求值bar( {println("AAA"); "AAA"} )bar( {println("AAA"); "AAA"} )传递的是表达式,而非结果,这里不被执行
  33. 33. 传值还是传名传值还是传名►1) by name1) by name 传递只出现在函数参数中传递只出现在函数参数中►2)2) 同上,同上, =>R=>R 类型只能出现在函数参数中类型只能出现在函数参数中表示表示 by name parameterby name parameter►3) => R3) => R 不能单纯看作是不能单纯看作是 ()=>R()=>R 的缩写,两的缩写,两者传递形式不同者传递形式不同
  34. 34. 高阶函数高阶函数
  35. 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)Unitscala> def f1() {println(1)}scala> def f1() {println(1)}f1: ()Unitf1: ()Unitscala> f2(f1)scala> f2(f1)11scala> f2(()=>println(scala> f2(()=>println(““hihi””)) //)) // 传入匿名函数传入匿名函数hihi
  36. 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+1res3: 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>2res9: Array[Int] = Array(3)res9: Array[Int] = Array(3)
  37. 37. 柯里化柯里化 (currying)(currying)
  38. 38. 柯里化柯里化 (currying)(currying)scala> def sum(x:Int, y:Int) = x+ysum: (x: Int, y: Int)Int// 参数打散,两个参数分开scala> def sum2(x:Int)(y:Int) = x+ysum2: (x: Int)(y: Int)Int
  39. 39. 柯里化柯里化 (currying)(currying)scala> sum2(1)(2)res1: Int = 3// 上面的调用相当于下面的几个步骤scala> def first(x:Int) = (y:Int)=>x+yfirst: (x: Int)Int => Intscala> first(1)res2: Int => Int = <function1>scala> val second = first(1)second: Int => Int = <function1>scala> second(2)res3: Int = 3
  40. 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. 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. 42. 柯里化柯里化 (currying)(currying)►模拟模拟 C#C# 里的里的 usingusing 自动释放资源的写法:自动释放资源的写法:using(res) {using(res) {handle(handle(……))}}resres 会被自动释放会被自动释放resres 必须是必须是 IDisposeableIDisposeable 接口子类接口子类
  43. 43. 柯里化柯里化 (currying)(currying)►模拟模拟 C#C# 里的里的 usingusingScalaScala 里的实现里的实现//// 暂忽略类型表达暂忽略类型表达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. 44. 柯里化柯里化 (currying)(currying)►模拟模拟 C#C# 里的里的 usingusing//// 使用使用 usingusingusing(Source.fromURL(cl.getResource(file))) {using(Source.fromURL(cl.getResource(file))) {stream => {stream => {for (line <- stream.getLines){for (line <- stream.getLines){……}}}}resultresult}}
  45. 45. 偏应用函数偏应用函数 (partial application function)(partial application function)也被翻译部分应用函数也被翻译部分应用函数把一个函数适配为另一个函数把一个函数适配为另一个函数
  46. 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)Doublescala> pow(2,3)scala> pow(2,3)res4: Double = 8.0res4: Double = 8.0scala> 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. 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)Unitscala> 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. 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. 49. 偏函数偏函数 (partial function)(partial function)注意和偏应用函数是两回事
  50. 50. 偏函数偏函数 (partial function)(partial function)图片来源图片来源 : http://developer.51cto.com/art/200912/166875.htm: http://developer.51cto.com/art/200912/166875.htm也被翻译为也被翻译为““部分函数部分函数””,区分于,区分于““完全函数完全函数””从数学上说,偏函数就是只实现了部分映射的函数:从数学上说,偏函数就是只实现了部分映射的函数:
  51. 51. 偏函数偏函数 (( 列子列子 ))1)1) 草原很好的解释了草原很好的解释了““偏函数偏函数””的概念以及用途:的概念以及用途:https://groups.google.com/forum/?fromgroups=#!https://groups.google.com/forum/?fromgroups=#!topic/scalacn/ASo80yip9fAtopic/scalacn/ASo80yip9fA2)2) 必须声明为必须声明为 PartialFunctionPartialFunction ;主要通过模式匹配;主要通过模式匹配来实现来实现
  52. 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 => hahares3: String = hahares3: String = haha
  53. 53. 一些谜题一些谜题 (puzzles)(puzzles)
  54. 54. 一些谜题一些谜题定义方法时省略小括号定义方法时省略小括号 ::scala> def m = "hi"scala> def m = "hi"m: String //m: String // 为什么不是为什么不是 ()String()Stringscala> 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. 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 MyClassscala> fooscala> fooscala> foo() //scala> foo() // 结果是什么?结果是什么?// foo// foo 与与 foo()foo() 的差异?的差异?
  56. 56. 一些谜题一些谜题►scalascala 里里 def foo() = xxxdef foo() = xxx 在调用时可以省略在调用时可以省略 ff 后边的后边的 ()() 但定义时如果不带小括号但定义时如果不带小括号 def foo = xxxdef foo = xxx 则调用则调用时加时加 ()() 要注意,要注意, foo()foo() 被翻译为了被翻译为了 (foo).apply()(foo).apply()
  57. 57. 一些谜题一些谜题►UnitUnit 的问题:的问题:1)1) 为何不用为何不用 javajava 的的 VoidVoid 类型,而引入类型,而引入 UnitUnit 类类型?型?a)a) 类型一致性类型一致性b) voidb) void 是面向函数的,是面向函数的, unitunit 除了可以是函数返回类型也可以除了可以是函数返回类型也可以是变量的类型。另,每个表达式是变量的类型。另,每个表达式 // 语句都有值,一些表达式语句都有值,一些表达式的值为的值为 unitunit
  58. 58. 一些谜题一些谜题Unit 的问题:val a = () => Unit // aval a = () => Unit // a 是什么类型?是什么类型?val b = () => {} //val b = () => {} // 有什么不同?有什么不同?注意:第一个匿名函数中的注意:第一个匿名函数中的 UnitUnit 是伴生对象是伴生对象
  59. 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. 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
  61. 61. 闭包闭包 (closure)(closure)
  62. 62. 闭包闭包跟函数有什么不同么?跟函数有什么不同么?
  63. 63. 闭包的本质:代码块闭包的本质:代码块 ++ 上下文上下文关于引用环境的绑定关于引用环境的绑定 (The Binding of(The Binding ofReferencing Environments)Referencing Environments) ,,先通过一个先通过一个 javajava 的匿名内部类来看:的匿名内部类来看:
  64. 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 的匿名内部类,和闭包很像。但用匿名内部类来实现,前的匿名内部类,和闭包很像。但用匿名内部类来实现,前提是先要定义好该行为的接口。繁琐一些,不那么灵活提是先要定义好该行为的接口。繁琐一些,不那么灵活
  65. 65. 闭包闭包► 闭包不可序列化闭包不可序列化► 要避免可变状态要避免可变状态
  66. 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. 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. 68. 闭包:更深入的了解闭包:更深入的了解The Binding of Referencing EnvironmentsThe Binding of Referencing Environments(( 引用环境的约束引用环境的约束 ))在递归的情况会是怎样的?在递归的情况会是怎样的?
  69. 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; // 子函数子函数 BBbeginbeginwriteln(I);writeln(I);end;end;begin (* A *)begin (* A *)if I > 1 thenif I > 1 thenPPelseelseA(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. 70. PascalPascal 里的深约束。在通过形式参数里的深约束。在通过形式参数 PP 调调用用 BB 时,存在着时,存在着 II 的两个实例。由于的两个实例。由于 PP 的的闭包是在闭包是在 AA 的第一个调用中创建的,因此的第一个调用中创建的,因此它使用该调用时的那个它使用该调用时的那个 II ,因此打印出,因此打印出11 。。------------------------以上摘自《程序设计语言以上摘自《程序设计语言————实践之路》第二版,引用环境的约束一节。实践之路》第二版,引用环境的约束一节。下面是一个同事把上面的代码翻译为下面是一个同事把上面的代码翻译为 C#C# ,看看是不是一样,他的回复:,看看是不是一样,他的回复:
  71. 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. 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. 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. 74. 参考参考http://delicious.com/w.hongjiang/closureshttp://delicious.com/w.hongjiang/closureshttp://james-iry.blogspot.comhttp://james-iry.blogspot.comhttp://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/woodcafehttps://twitter.com/woodcafehttps://twitter.com/woodcafehttp://www.laiwang.com/u/3001http://www.laiwang.com/u/3001http://www.slideshare.net/hongjiang //ppthttp://www.slideshare.net/hongjiang //ppt 和和 pdfpdfhttp://hongjiang.info (http://hongjiang.info ( 内容后续放入内容后续放入 ))

×