Scala 按名称调用混淆 [英] Scala Call By Name Confusion

查看:38
本文介绍了Scala 按名称调用混淆的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 REPL 编写一些按名称调用的示例,并在 Eclipse 中运行相同的示例.

I am working on some call by name examples using the REPL and running the same examples in Eclipse.

这是 Eclipse 中的内容:
场景 1:

Here is what in Eclipse:
Scenario 1:

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

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

输出为:调用函数

场景二:
方法takesFunct保持不变

Scenario 2:
the method takesFunct stays the same

takesFunct({println("Calling funct")}

输出为:
调用函数
调用函数

The output is:
Calling funct
Calling funct

Scala REPL场景 1:

Scala REPL Scenario 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与上面定义的方法相同

Scenario 2 The same method as defined above

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

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

Two questions
1) Why is the output from Eclipse different from the REPL?
2) What is the difference between passing in

val funct = {...}
takesFunct(funct)

相对于

takesFunct({...})

推荐答案

在@IttayD 的回答后更新:

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

The scenario 1 on Eclipse is right, you'll see why below. Scenario 2 is clearly an Eclipse bug. ScalaIDE on Eclipse is known for its brokenness. I wouldn't trust it (or use it). Use Intellij IDEA's Scala plugin if you must.

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

The answer to both of your questions is that, {} is a block that returns the return type of it's last statement. It's exactly the same as Scheme's (begin) or Common Lisp's (progn). When you have:

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's RHS has already been eagerly evaluated and returned a value () of type Unit to funct. Applying an already computed value to a call-by-name function and using it in the body doesn't cause reevaluation because the value is already a leaf.

进一步更新:

def takesFunct(f: => Unit)

def takesFunct(f: () => Unit)

在某些圈子中被称为流式或延迟评估.但是,有一个主要区别在于您调用提供的参数的方式.在后一种情况下,为了从 f 取回一个值,你必须像这样调用它 - 即 f().在前一种情况下,f 是一个 懒惰表达式,当它第一次被引用时计算为一个值,因此 call-by-name.你可以想到语法 f: =>;Unit 作为将您提供的任何表达式自动包装在容器 {} 中的一种方式.使用时检索其内容,如下所示:

which is known as streaming or delayed evaluation in certain circles. There is one major difference though, which lies in the way you invoke the supplied argument. In the latter case, in order to get back a value from f, you have to invoke it as such - i.e f(). In the former case, f is a lazy expression that evaluates to a value when it is first referenced as such, hence call-by-name. You can think of the syntax f: => Unit as a way to automatically wrap whatever expression you supply in a container {}. The contents of which is retrieved when used like so:

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.让我们再做一个实验:

This is because now you are creating a block in-place that is bound to the function's parameter f, and it is only evaluated when you use it in val b = f. Let's do one more experiment:

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

你怎么问?因为 println(...) 被包裹在绑定到 f{} 中.引用f 检索容器内的值,即println(...) 的值,即():Unit.在前面的例子中,f 被绑定到 { { println(...) } },这和 { println(...) } 是一样的,所以你得到相同的结果.事实上,您可以无限期地嵌套 {} 并且仍然得到相同的结果.唯一的区别是,手动提供 {} 可以让您像这样放入多个语句:

How come you ask? Because println(...) was wrapped in a {} that is bound to f. Referencing f retrieves the value inside the container, which is the value of println(...), which is ():Unit. In the previous example, f was bound to { { println(...) } }, which is the same as { println(...) }, so you get the same result. In fact you can nest {} indefinitely and still get the same thing back. The only difference is, manually supplying {} lets you put multiple statements inside like so:

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

希望这会有所帮助.

这篇关于Scala 按名称调用混淆的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆