Scala中的Apply和Lambda函数

3
我有以下代码片段:
scala> val builder = new StringBuilder("foo bar baz ")
builder: StringBuilder = foo bar baz

scala> (0 until 5) foreach { builder.append("!") }

scala> builder.toString
res15: String = foo bar baz !

I guess that in reality something like

(0 until 5) foreach builder.append("!").apply

发生了什么。有人能解释一下为什么会发生这种情况,或者如果我错了,那么真正发生了什么?

1
我正在写一个答案,突然意识到在这种情况下(0 until 4) foreach { builder.append("!").apply }(0 until 4) foreach { builder.append("!").apply(_) }的行为不同,但我无法解释。 - Sascha Kolberg
2个回答

7
为了理解正在发生的事情,我使用了我的小工具scala-to-java
您的代码转换为Java后如下所示:
public final class _$$anon$1 {
  private final StringBuilder builder = new StringBuilder("foo bar baz ");

  private StringBuilder builder() {
    return this.builder;
  }

  {
    RichInt$.MODULE$.until$extension0(Predef$.MODULE$.intWrapper(0), 5).foreach((Function1<Object, Object>)this.builder().append("!"));
    this.builder().toString();
  }
}

正如您所看到的,builder.append("!") 的结果是一个 StringBuilder,它被强制转换为 Function1 并作为函数参数在 foreach 中使用。
显然,StringBuilder 确实是一个函数,如果你检查它的类型层次结构:
- StringBuilder (scala.collection.mutable) - AbstractSeq (scala.collection.mutable) - AbstractSeq (scala.collection) - Seq (scala.collection) - PartialFunction (scala) - Function1 (scala)
因此,我们可以得出以下结论:
- 您可以将任何 StringBuilder 用作从 IntChar 的函数或部分函数。 - 在您的情况下,append 只会被评估一次,它的结果 (StringBuilder) 就会被用作函数。

1
非常感谢!我刚从 Kotlin 切换到 Scala,原本在 Kotlin 中的一个函数,在 Scala 中变成了一段代码块,巧合的是它返回了一个适当类型的函数。 - Yaroslav

2

foreach期望从您提供的(Int => Unit)函数中获取一个函数,但是您提供了() => StringBuilder。然而,由于StringBuilder是一个集合,因此在foreach循环中选择了它的'apply'函数来检索特定索引处的字符作为要应用的函数。

您可以通过以下方式修复代码:

(0 until 5) foreach { _ => builder.append("!")}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接