为什么call-by-name参数需要类型为Int的参数而不是()=>诠释

时间:2015-03-22 02:20:50

标签: scala callbyname

我对在Scala中使用call-by-name参数感到有些困惑。请帮我理解这里发生了什么。请考虑以下使用call-by-name参数的示例:

  def param = {println("Param evaluates"); 40}
  def lazyEval(x: => Int) = {
    println("lazy"); 
    x 
  }
  val s = lazyEval(param + 2)
  // s = 42

我有几个问题彼此相关:

  1. lazyEval方法需要=> Int类型的参数,那么为什么param + 2操作合法?为什么我们可以在调用=> Int方法时将整数2添加到类型为<function0>的对象(我的理解是lazyEval)?正如IDE提示我的那样,lazyEval函数需要Int类型的参数而不是=> Int(这到底是什么?)。

  2. 为什么在将回调类型从=> Int更改为() => Int代码后无法编译?这两种类型有什么不同吗?我虽然短版本('=&gt; Int')只是一个语法糖。

  3. 在玩了一些代码后,我终于可以更改代码,以便用() => Int进行编译。这种方式对我来说更直观。

  4.   def param = {println("Param evaluates"); 40}
      def lazyEval(x: () => Int) = { // changed type to '() => Int'
        println("lazy"); 
        x() // explicitly calling function using parentheses '()'
      }    
      val s = lazyEval(param _) // adding underscore after method name and removing `+2`
    

    此版本与第一版(回调=> Int类型)之间有何区别?为什么在这个版本中我们不能添加值为2的整数和param函数(我的意思是lazyEval(param _ + 2))?什么意思是方法名称后面的下划线? (我猜它曾用于传递方法本身,而不是返回值)

    感谢您的帮助

3 个答案:

答案 0 :(得分:3)

  

那么为什么param + 2操作合法?为什么我们可以使用type =&gt;向对象添加整数2 INT

我们可以将2添加到param,因为它评估为Int,您正在Int添加Intparam不是函数=> Int,它是一种方法,因此param + 2 => Int。即。表达式,其值为Int

  

为什么在更改回调类型后从=&gt; Int to()=&gt; Intcode没有编译?这两种类型有什么不同吗?我虽然简短的版本(&#39; =&gt; Int&#39;)只是一个语法糖。

=> Int() => Int并不意味着同样的事情。一个是评估为Int的任何内容,另一个是UnitInt的函数。 2不是() => Int,而是() => 2。或者您也可以说2=> Int

  

为什么在这个版本中我们不能添加值为2的整数和param函数(我的意思是thislazyEval(param _ + 2))?什么意思是方法名称后面的下划线? (我猜它曾用于传递方法本身,而不是返回值)

param是一种方法,而不是一种功能。在这种情况下,下划线将param提升为一个函数。因此param _是一个函数() => Int。这正是我们无法向其添加2的原因,因为您无法将2添加到某个功能中。基本上你认为(1)不应该工作的确切原因。


总结:

def lazyEval(x: => Int)是一个参数x的方法,可以任何评估为Int 的内容。这可以是返回Int的任何方法,具体值Int,或解析为Int的某些代码块等。

lazyEval(x: () => Int)是一个参数x的方法, 只能是一个返回Int的无参数函数。这可能意味着方法param被提升到一个函数,或者像() => 2那样奇怪的东西。但它必须是一个功能。因此,简单地传递像2这样的值是行不通的。

答案 1 :(得分:2)

正如@mz指出的那样,value: => T可以被认为是语法,用于创建包装给定表达式的方法

object Magician {
  def magic(param: => Int) = param
}

object App {
  val result: Int = Magician.magic(3 + 3)
}

将(粗略地)翻译为:

object App {
    private[this] def magic$param$1: Int = 3 + 3
    val result: Int = Magician.magic(magic$param$1 _)
}

call-by-name参数的行为类似于无参数方法定义 - 引用任一导致被调用的方法:

def paramlessMethod = 3 + 3
def callByName(param: => Int) = param + paramlessMethod
def test() = callByName(5 + 5) //  16, always

在这两种情况下,你都可以解除&#34; Function0的方法(或者#34;推迟评估方法&#34;如果你喜欢这样思考的话)使用魔法_

def paramlessMethod = 3 + 3
val functionWrapper: Function0[Int] = paramlessMethod _
functionWrapper() // 6

def callByName(param: => Int) = param _
val functionFromParam: Function0[Int] = callByName(3 + 3)
functionFromParam() // 6

答案 2 :(得分:1)

此处解释def i: Int的类型:

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

此处解释了名称参数i: => Int的类型:

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

  

这样的参数的类型是无参数方法type =&gt;   吨。

换句话说,它与方法类型def i: Int相同。