2010-07-10 36 views
7

刚刚看到一个有趣的可能性初始化代码块Scala中高阶功能,如的foreach或地图:阶的foreach和地图初始化

(1 to 3) map { 
    val t = 5 
    i => i * 5 
} 


(1 to 3) foreach { 
    val line = Console.readLine 
    i => println(line) 
} 

这是一些记录功能,或者我应该避免这样的构造?我可以想象,“初始化”块进入构造函数,闭包本身变成一个apply()方法?

感谢帕特为原来的问题(http://extrabright.com/blog/2010/07/10/scala-question-regarding-readline

回答

12

虽然使用的功能并不少见,但我承认这是一个相当奇特的功能组合。基本技巧是Scala中的任何块都是一个表达式,类型与块中的最后一个表达式相同。如果最后一个表达式是一个函数,这意味着该块具有功能类型,因此可以用作“映射”或“foreach”的参数。在这些情况下会发生什么情况是,当调用“map”或“foreach”时,将对块进行评估。该块评估为一个函数(第一种情况下i => i * 5),然后将该函数映射到范围内。

该构造的一个可能用途是块定义可变变量,并且每次调用变量时,结果函数都会变量变量。变量将被初始化一次,被函数关闭,并且每次函数被调用时它们的值都会更新。

例如,这里的计算第一6个阶乘号码

(1 to 6) map { 
     var total = 1 
     i => {total *= i;total} 
    } 

(顺便说一句,对不起使用阶乘作为一个例子的一个有点令人惊讶的方式,这是要么或斐波纳契。功能 预设电台协会规则。你得面对这个问题,在大厅里把它和男孩一起拿下来。)

有一个块返回一个函数的一个不太必要的理由是在块的前面定义辅助函数。举例来说,如果你的第二个例子是不是

(1 to 3) foreach { 
    def line = Console.readLine 
    i => println(line) 
} 

的结果将是三条线中读取和呼应各一次,而你的例子有行读取一次,呼应三次。

+0

比我的答案要精确得多。 +1 – VonC 2010-07-10 21:10:28

+0

在因子的例子中,你应该使用'total * = i'而不是引入第二个变量'counter' – 2010-07-11 20:47:01

+0

是的,我后来才意识到。将编辑 – 2010-07-12 02:09:34

1

首先,原来的博客“Scala Question Regarding readLine”后提

的评论的“line”是一个值,不能被执行,这是从“Console.readLine”方法执行的结果中仅分配一次。
它在关闭时使用少于三次。
但是,如果你把它定义为一种方法,它会被执行三次:

(1 to 3) foreach { 
    def line = Console.readLine 
    i => println(line) 
} 

的博客Scala for Java Refugees Part 6: Getting Over Java对高阶函数一个有趣的部分,包括:

斯卡拉提供仍这些高阶函数的语法更灵活。
在迭代调用中,我们正在创建一个完整的匿名方法,以便再次调用println(String)方法。
考虑到println(String)本身是一种需要String并返回Unit的方法,人们会认为我们可以压缩这一点。事实证明,我们可以:

iterate(a, println) 

由于省略了括号,只是指定方法的名称,我们告诉我们要使用println作为功能值,Scala编译器,通过它到iterate方法。
因此,我们不是创建一个新的方法来处理一组调用,而是传入一个已经做到了我们想要的旧方法。
这是C和C++中常见的模式。实际上,将函数作为函数值传递的语法完全相同。似乎有些东西永远不会改变...