【问题标题】:Using apply ("()") on function passed in by-name parameter: evaluation is not forced?在按名称参数传递的函数上使用apply(“()”):不强制评估?
【发布时间】:2013-01-03 23:44:39
【问题描述】:

我有一个函数:

def nanoTime() = {
    println("Getting nano time...")
    System.nanoTime // returns nanoTime
}

还有另一个函数,它接受一个函数

def printTime(time: => Long) = {  // indicates a by-name parameter
    println(">> delayed()")
    println("Param: " + time)
    time // returns time
}

现在是这样。当我这样做时:

scala> printTime(nanoTime())
>> delayed()
Getting nano time...
Param: 546632085606127
Getting nano time...
res11: Long = 546632086131624

当我这样做时,我得到了相同的结果:

scala> printTime(nanoTime)
>> delayed()
Getting nano time...
Param: 546622367510997
Getting nano time...
res10: Long = 546622368149903

两者之间没有区别:

scala> printTime(nanoTime())

scala> printTime(nanoTime)

所以传递函数名和传递函数名后跟()是没有区别的。是不是一直都是这样,还是这个case有什么特别之处?

谢谢。

【问题讨论】:

  • 不确定是否完全重复,但这个问题很接近:stackoverflow.com/questions/6939908/…
  • 我不认为它是重复的,尽管它是相关的。此问题明确关注按名称参数,因此应在答案中明确说明此类等效(或缺少)。
  • (虽然我确信这其中有很多漏洞,但我通常将按名称调用视为创建匿名函数内部传递的参数的隐式包装。如果这样一个简单的视图是当x 等同于x() 时,{() => x}{() => x()} 可以被视为等效代理。)
  • @pst:这是不可取的。可以像匿名的零参数函数一样实现按名称参数,但它们非常不同。特别是,您无法获得代表按名称实际参数的“函数”。 所有您可以做的是评估实际参数表达式(任意多次),而不是像零参数函数那样通过添加 () 后缀来完成。跨度>

标签: scala callbyname


【解决方案1】:

Scala 有参数列表的概念,其中一个方法可能需要多个参数。但是,为了方便起见,它也允许省略终端空参数列表。所以

f
f()
f()()

可能都是一样的——直到您查看f 才知道。别名参数的作用是延迟代码块的执行。现在,正式地,如果我们有

def f0: String = "salmon"
def f1(): String = "herring"
def f2()(): String = "halibut"

那么你会期望f0 匹配一个按名称参数,而其他参数不匹配,如果转换为一个函数。具体来说,您会期望

f0   <==>   => String
f1   <==>   () => String
f2   <==>   () => () => String

转换时。让我们看看通过f _ 请求时实际发生了什么:

scala> f0 _
res4: () => String = <function0>

scala> f1 _
res5: () => String = <function0>

scala> f2 _
res6: () => () => String = <function0>

哦,好吧; f0 实际上转换为一个带有一个空参数块而不是零的函数(这是一个名称参数的样子)。所以事实证明,您的 by-name 参数根本没有将您的方法转换为函数——类型签名不匹配!

因此,它的原因如下:

// I need a code block that returns a long
nanoTime             // Wait, there is no nanoTime exactly
nanoTime()           // Aha, that works!  Must have meant that
: => { nanoTime() }  // There, nicely packaged.

你看不出区别的原因是,为了返回一个Long,by-name 参数已经填充了缺失的(),但随后将整个东西包装在一个代码块中以便稍后执行。

(另请注意,名称参数实际上只是 Function0 在引擎盖下——也就是说,x: =&gt; A 实际上是 x: () =&gt; A——以及“零参数块”的东西只是一个编译器虚构。实际上,所有参数块都是一个编译器虚构——JVM 只知道一个参数列表。正是这种无块虚构,再加上 who-cares-about-empty-parens 虚构,这会导致观察到的行为。)

如果你从一个空的参数块请求一个函数,那么事情是这样的:

def printF(f: () => String) = println(f())

scala> printF(f0)
<console>:23: error: type mismatch;
 found   : String
 required: () => String
              printF(f0)
                     ^

scala> printF(f1)
herring

scala> printF(f2)
<console>:23: error: type mismatch;
 found   : () => String
 required: String
              printF(f2)

scala> printF(f2())
halibut

现在在哪里括号 do 很重要,因为编译器正在尝试将方法签名与函数签名匹配。按名称参数情况的特殊情况不再适用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-08-02
    • 2019-07-02
    • 1970-01-01
    • 2015-02-16
    • 1970-01-01
    • 2015-01-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多