7

刚刚看到在 Scala 中为 foreach 或 map 等高阶函数初始化代码块的有趣可能性:

(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

4

2 回答 2

12

虽然使用的功能并不少见,但我承认这是一个相当奇怪的功能组合。基本技巧是 Scala 中的任何块都是一个表达式,其类型与块中的最后一个表达式相同。如果最后一个表达式是一个函数,这意味着该块具有函数类型,因此可以用作 "map" 或 "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)  
}

结果将是读取三行并每行回显一次,而您的示例将行读取一次并回显三次。

于 2010-07-10T20:55:29.850 回答
1

一、原博客“关于readLine的Scala Question ”帖子中的评论提到

“<code>line”是一个值,不能执行,它只从“<code>Console.readLine”方法执行的结果中赋值一次。
它在您的封闭中使用不到 3 次。
但是如果你把它定义为一个方法,它会被执行3次:

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

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

Scala 为这些高阶函数的语法提供了更多的灵活性。
在迭代调用中,我们创建了一个完整的匿名方法,只是为了再次调用该println(String)方法。
考虑到println(String)is 本身是一个接受 aString并返回的方法Unit,人们会认为我们可以将其压缩一点。事实证明,我们可以:

iterate(a, println)

通过省略括号并仅指定方法名称,我们告诉 Scala 编译器我们希望将其println用作函数值,并将其传递给iterate方法。
因此,我们不是只创建一个新方法来处理一组调用,而是传入一个已经完成我们想要的旧方法。
这是 C 和 C++ 中常见的模式。实际上,将函数作为函数值传递的语法完全相同。似乎有些事情永远不会改变……</p>

于 2010-07-10T20:55:10.577 回答