Scala通过名称混淆呼叫

时间:2011-02-13 20:11:23

标签: scala

我正在使用REPL通过名称示例进行一些调用,并在Eclipse中运行相同的示例。

以下是Eclipse中的内容:
场景1:

val funct = {println("Calling funct")}
takesFunct(funct)

def takesFunct(f: => Unit)
{
   val b = f
}

输出为:调用函数

情景2:
方法takeFunct保持不变

takesFunct({println("Calling funct")}

输出结果为:
    呼唤功能
    呼叫功能

Scala REPL 场景1:

scala> def takesFunct(f: => Unit)
{
  val b = f
}
takesFunct: (f: => Unit)Unit

scala> val funct = {println("Calling funct")}
Calling funct
funct: Unit = ()

scala> takesFunct(funct)
// No Output

情景2 与上面定义的方法相同

scala> takesFunct({println("Calling funct")}
Calling funct

两个问题
1)为什么Eclipse的输出与REPL不同?
2)传入

之间有什么区别
val funct = {...}
takesFunct(funct)

而不是

takesFunct({...})

3 个答案:

答案 0 :(得分:9)

在@ IttayD回答后更新:

Eclipse上的场景1是对的,你会在下面看到原因。场景2显然是一个Eclipse错误。 Eclipse上的ScalaIDE因其破碎而闻名。我不相信(或使用它)。如果必须,请使用Intellij IDEA的Scala插件。

你的两个问题的答案是,{}是一个块,它返回它最后一个语句的返回类型。它与Scheme的(begin)或Common Lisp的(progn)完全相同。当你有:

scala> def takesFunct(f: => Unit)
{
  val b = f
}
takesFunct: (f: => Unit)Unit

scala> val funct = {println("Calling funct")}
Calling funct
funct: Unit = ()

scala> takesFunct(funct)
// No Output

funct已经热切地评估了RHS,并将()类型的值Unit返回到funct。将已计算的值应用于按名称调用的函数并在正文中使用它不会导致重新评估,因为该值已经是叶子。

进一步更新:

def takesFunct(f: => Unit)

具有基本相同的语义
def takesFunct(f: () => Unit)

在某些圈子中称为流式传输或延迟评估。但是有一个主要的区别,就是你调用提供的参数的方式。在后一种情况下,为了从f获取值,您必须按原样调用它 - 即f()。在前一种情况下,f是一个延迟表达式,当它首次被引用时会计算为一个值,因此按名称调用。您可以将语法f: => Unit视为自动包装您在容器{}中提供的任何表达式的方法。使用时检索其内容:

scala> val a = { 1 } // 1 wrapped in {}, and retrieved when assigned to a
a: Int = 1

那么这个呢?

scala> takesFunct({println("Calling funct")})
Calling funct

这是因为现在您正在创建一个绑定到函数参数f的就地块,并且仅在val b = f中使用它时进行评估。我们再做一次实验:

scala> takesFunct(println("Calling funct"))
Calling funct

你怎么问?因为println(...)包含在绑定到{}的{​​{1}}中。引用f检索容器内的值,即f的值,即println(...)。在上一个示例中,():Unit绑定到f,与{ { println(...) } }相同,因此您获得的结果相同。事实上,你可以无限期地嵌套{ println(...) }并仍然可以得到同样的东西。唯一的区别是,手动提供{}允许您将多个语句放入其中:

{}

希望这有帮助。

答案 1 :(得分:2)

输出没有区别。不同之处在于你想要什么。从Eclipse中你运行了两件事:

val funct = {println("Calling funct")} // prints Calling funct here
takesFunct(funct)

def takesFunct(f: => Unit)
{
   val b = f
}

val funct = {println("Calling funct")} // prints Calling funct here
takesFunct({println("Calling funct")}

def takesFunct(f: => Unit)
{
   val b = f                           // prints Calling funct here
}

在REPL上,根据您自己的日志发生了同样的事情:

scala> def takesFunct(f: => Unit)
{
  val b = f
}
takesFunct: (f: => Unit)Unit

scala> val funct = {println("Calling funct")}
Calling funct
funct: Unit = ()

scala> takesFunct(funct)
// No Output

现在,在第二个场景中你所做的就是:

scala> takesFunct({println("Calling funct")}
Calling funct

由于您没有重复val funct任务,这仍然存在于Eclipse的第二个场景中,因此它没有打印消息。

请注意

val funct = {println("Calling funct")}
实际上,

等同于

println("Calling funct")
val funct = ()

答案 2 :(得分:0)

场景1:正如预期的那样。输出来自第一行,而不是调用方法。

场景2:我没有Eclipse,但听起来不对。

REPL场景1:您无法按名称值定义呼叫。你所做的是将表达式{println("Calling funct")}的最后一个值赋值给funct。最后一个值是println的结果,即Unit。因此,takeFunct会收到一个评估为Unit的thunk,因此不打印任何内容。

REPL场景2:正如预期的那样