【问题标题】:Scala: Passing Vs applying functionScala:传递 Vs 应用函数
【发布时间】:2014-08-08 18:08:40
【问题描述】:

假设我们有以下代码sn-p:

  List(1, 2, 3) 
  .map(doubleIt) // passing function
  .map(x => doubleIt(x)) // applying function

   def doubleIt(i: Int): Int = 2 * i

如您所见,我们可以将 doubleIt 作为函数文字传递,也可以将其应用到另一个匿名 Lambda 中。我一直想知道哪种方法更好。我个人更喜欢传递函数文字,因为似乎第二种方法最终会无缘无故地创建一个额外的包装器 Lambda,但我并不是 100% 肯定我的推理是正确的。

我很想知道每种风格的优缺点是什么,以及哪种风格肯定比另一种更好。

【问题讨论】:

  • 术语的小点:第二个是“函数字面量”,但不是第一个。
  • 其实这两种形式是一样的。 Scala 编译器将第一种形式隐式转换为第二种形式。两者都告诉 Scala 编译器通过将doubleIt 函数应用于列表的每个成员来创建一个新列表。至于风格,第二种形式更冗长,因此具有更明确的行为,而第一种形式更简洁,可能更受Scala老手的青睐。我猜这是个人喜好问题。
  • 另外,如果doubleIt 没有在其他地方使用,我个人更喜欢第三个选项:List(1,2,3).map{_ * 2}
  • @MikeAllen 这不太对;看我的回答。

标签: function scala


【解决方案1】:

这在 Scala 2.12+ 中可能会改变,但目前这两种方法是相同的。作为测试,我创建了以下内容:

class Test {
  def testPassingFunction: List[Int] = List(1, 2, 3).map(doubleIt)

  def testApplyingFunction: List[Int] = List(1, 2, 3).map(x => doubleIt(x))

  def doubleIt(i: Int): Int = 2 * i
}

然后我编译它并使用javap 反汇编字节码。这两个函数是相同的(除了不同的字符串。在所有情况下,都会创建一个从Function1 扩展的新类,该类调用适当的方法。正如@Mike 在 cmets 中所说,Scala 编译器将所有内容转换为第二种形式。

【讨论】:

  • 出于好奇,您是否测试过如果doubleIt 已经是函数值会发生什么?
  • @AaronNovstrup 是正确的。如果将doubleIt 定义为val doubleIt = (i: Int) => 2*i,则doubleIt 已经是Function1 类型的值,因此在List(1,2,3).map(doubleIt) 中不需要换行。但是,在List(1,2,3).map(x => doubleIt(x)) 中,您明确告诉编译器创建一个新的Function1 对象,该对象实质上是将调用转发到您已经拥有的Function1 对象。
【解决方案2】:

事实证明,这在一定程度上取决于您的“功能”是什么。如果它实际上是一个函数(即一个函数值,定义为val doubleIt = (x: Int) => 2 * x),那么你的预感是正确的。您传递函数文字的版本仅应用 doubleIt(即,l map { x => doubleIt(x) } 按编写方式编译,从而产生一个委托给 doubleIt 的匿名函数。传递 doubleIt 作为函数值取出中间人。另一方面,如果doubleIt 是一个方法,那么两种形式的编译方式相同。

您可以在 REPL 自己轻松地验证这一点。定义以下类:

class A {
   val l = List(1,2,3)
   val f = (x: Int) => 2 * x
   def g(x: Int) = 2 * x

   def m1 = l map f
   def m2 = l map { x => f(x) }
   def m3 = l map g
   def m4 = l map { x => g(x) }
}

然后运行:power:javap -v A

也就是说,除了对性能最关键的代码之外,这种区别不太可能对任何代码产生实际影响。在一般情况下,代码清晰度是更重要的考虑因素,并且在某种程度上取决于将来谁将阅读您的代码。就个人而言,我更喜欢简洁的lst map doubleIt 形式;这种形式消除了一堆没有语义的句法噪音。我想较长的形式可能被认为更明确,特别是对于不太熟悉map 方法的开发人员。字面意思非常符合意图:“(给定)列表,将(每个)x映射到doubleIt(x)”。您的团队必须决定什么对您和您的组织最有利。

【讨论】:

    猜你喜欢
    • 2016-02-03
    • 1970-01-01
    • 2019-06-19
    • 2015-07-18
    • 2015-02-10
    • 2015-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多