繁体   English   中英

Scala通过名称混淆呼叫

[英]Scala Call By Name Confusion

我正在使用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({...})

在@ 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的值() 将已计算的值应用于按名称调用的函数并在正文中使用它不会导致重新评估,因为该值已经是叶子。

进一步更新:

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(...)被包裹在绑定到f{}中。 引用f检索容器内的值,即println(...)的值,即():Unit 在前面的示例中, f绑定到{ { println(...) } } ,这与{ println(...) }相同,因此您得到相同的结果。 事实上,你可以无限期地嵌套{} ,但仍然会得到同样的东西。 唯一的区别是,手动提供{}允许您将多个语句放入其中:

scala> takesFunct({ println("hello"); println("world") })
hello
world

希望这可以帮助。

输出没有区别。 不同之处在于你想要什么。 从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赋值,因此它没有打印消息。

注意

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

实际上,是等同于

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

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

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

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

REPL场景2:正如所料

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM