为什么多次调用此函数?

时间:2014-07-16 21:48:24

标签: scala

在此功能中" f" :

def f(x: => Int) : Int = x * x * x  //> f: (x: => Int)Int

var y = 0                           //> y  : Int = 0

f {
    y += 1
    println("invoked")
    y
}                                   //> invoked
                                    //| invoked
                                    //| invoked
                                    //| res0: Int = 6

" F"被调用的次数与" x"相同。参数乘以。

但为什么多次调用函数?

应该" f"不扩展为1 * 1 * 1而不是1 * 2 * 3

5 个答案:

答案 0 :(得分:3)

您的x不是函数,它是一个名称参数,其类型是无参数的方法类型。

无参数方法类型与def x的含义相同,每次引用时都会对其进行评估。我们所指的是x而不是x.apply()x()

每次f中引用x时,都会评估您传递给函数f的表达式。这个表达式是大括号中的整个表达式,一个块表达式。块是一系列语句,后面是结果表达式。

以下是另一种解释:https://stackoverflow.com/a/13337382/1296806

但是,让我们不要把它称为一个功能,即使它的行为类似于一个功能。

以下是规范中使用的语言:

http://www.scala-lang.org/files/archive/spec/2.11/04-basic-declarations-and-definitions.html#by-name-parameters

它不是值类型,因为您无法写val i: => Int

当他们更改实现时,这是一个大问题,因此您可以将名称arg传递给另一个方法,而无需先对其进行评估。从来没有一个问题可以像这样传递函数值。例如:

scala> def k(y: => Int) = 8
k: (y: => Int)Int

scala> def f(x: => Int) = k(x)   // this used to evaluate x
f: (x: => Int)Int

scala> f { println("hi") ; 42 }
res8: Int = 8

一个例外是"保留了名字的行为"传入的x。

由于eta扩张,这对人们很重要:

scala> def k(y: => Int)(z: Int) = y + y + z
k: (y: => Int)(z: Int)Int

scala> def f(x: => Int) = k(x)(_)  // normally, evaluate what you can now
f: (x: => Int)Int => Int

scala> val g = f { println("hi") ; 42 }
g: Int => Int = <function1>

scala> g(6)
hi
hi
res11: Int = 90

问题是你期待多少问候?

更多怪癖:

scala> def f(x: => Int) = (1 to 5) foreach (_ => x)
f: (x: => Int)Unit

scala> def g(x: () => Int) = (1 to 5) foreach (_ => x())
g: (x: () => Int)Unit

scala> var y = 0
y: Int = 0

scala> y = 0 ; f { y += 1 ; println("hi") ; y }
hi
hi
hi
hi
hi
y: Int = 5

scala> y = 0 ; g { y += 1 ; println("hi") ; () => y }
hi
y: Int = 1

scala> y = 0 ; g { () => y += 1 ; println("hi") ; y }
hi
hi
hi
hi
hi
y: Int = 5

功能不会导致此问题:

scala> object X { def f(i: Int) = i ; def f(i: => Int) = i+1 }
defined object X

scala> X.f(0)
res12: Int = 0

scala> trait Y { def f(i: Int) = i }
defined trait Y

scala> object X extends Y { def f(i: => Int) = i+1 }
defined object X

scala> X.f(0)
<console>:11: error: ambiguous reference to overloaded definition,
both method f in object X of type (i: => Int)Int
and  method f in trait Y of type (i: Int)Int
match argument types (Int)
              X.f(0)
                ^

比较方法类型:

http://www.scala-lang.org/files/archive/spec/2.11/03-types.html#method-types

这不是一个迂腐的区别;无论当前的实现如何,将名字参数视为&#34;真的&#34;可能会令人困惑。一个功能。

答案 1 :(得分:1)

另一种说法已经说过的方法是在f内部调用函数x三次。第一次增加y var并返回1.第二次它再次增加y返回2,第三次再增加y并返回3.

如果您只想调用一次,那么您可能需要执行以下操作:

def f(x: => Int) : Int = x * x * x
var y = 0

lazy val xx = {
    y += 1
    println("invoked")
    y
}

f {xx}

这将打印&#39;调用&#39;只有一次并导致返回值为1。

答案 2 :(得分:1)

x:T表示需要T值。

x:=&gt; T表示需要一个T值,但它是按名称调用的。

x:()=&gt; T这意味着需要一个没有给T

的函数

然而,这个问题与功能和方法之间的区别无关。

原因是每次尝试使用时都会调用名称调用。

更改为按值 def f(x:Int):Int 调用,它只会调用一次。

答案 3 :(得分:0)

函数f()返回的结果正在改变,因为有一个全局变量随着对该函数的每次后续调用而递增。

f(x:=&gt; Int)中的x被解释为“返回Int的某个函数”。因此,必须调用3次来评估x*x*x表达式。每次调用时,都会递增全局变量并返回结果,这是您到达后续三个自然数的方式(因为全局变量初始化为0)。因此1 * 2 * 3.

答案 4 :(得分:0)

因为每次在y

中使用参数时,都会将f递增1