【问题标题】:In Scala invoking no-parameter function with and without brackets is executed in different way在Scala中调用带和不带括号的无参数函数以不同的方式执行
【发布时间】:2015-02-28 13:16:10
【问题描述】:

我有以下 Currying 函数声明:

def logString(count: Int)(fun:() => Unit) {
  for (n <- 1 to count) { fun }
}

我是这样调用这个函数的:

logString(3) { () => print("I") }

结果什么都没有——只是没有输出。

然后我只是在“有趣”函数调用之后添加括号,在 Currying 函数声明的主体内:

def logString(count: Int)(fun:() => Unit) {
  for (n <- 1 to count) { fun() }
}

结果变成了预期:

III

这是 Scala 的 bug,还是我在学习 Scala 时遗漏了一些规则?

我知道当你声明这样的函数时,有一条规则: 定义我的乐趣 = 1 我们不能用括号调用它——编译失败。 但是在调用带和不带括号的函数时产生不同的结果似乎更像是一个错误。

我是对的还是我错过了有关 Scala 的一些东西?

【问题讨论】:

  • 我认为fun在这种情况下只是一个val,就像你会写val x = 10,然后在一个循环中for (n &lt;- 1 to 5) { x }
  • 这也是我发布问题时的想法。但似乎不是真的。我做了以下更改: def logString(count: Int)(fun:() => Unit) { for (n ; ; ;
  • 是的,确切地说,因为fun 是一个值定义,当您在其上调用+ 时,因为它的类型为Function0(而不是String 例如)它是通过调用toString 方法转换为字符串。然后您将获得&lt;function0&gt;,并在其上附加;。这发生了 3 次。
  • @myQs - 如果 fun 是一个值,这就是您所期望的 - fun + ";" 被评估为 fun.toString() + "; " 并且 fun.toString&lt;function0&gt;fun 未在 print 语句中进行评估。
  • @myQs - 是的,funFunction0 类型的值 - 函数是 scala 中的值,就像 Ints 或 Strings 是值一样。 toString 只返回值的 String 表示。对于Function0 实例,这是&lt;function0&gt; - 请参阅source

标签: scala currying


【解决方案1】:

查看fun 的类型,它是fun: () =&gt; Unit。你可以认为这意味着当你用() 调用它时,你会得到Unit 作为回报。在没有显式调用它的情况下,fun函数作为一个值引用,而不是调用它的结果。这就是高阶函数概念的精髓。

如果它的类型为 fun: =&gt; Unit,只需提及 fun 就会导致它被执行,在这种情况下,将无法将函数作为值引用。

【讨论】:

    【解决方案2】:

    当你有这样的声明时

    val x = 10
    

    这是一个价值定义。在这种情况下,值 x 可以是整数, 但它也可以是一个函数。

    val y = () => println("i'm a function")
    

    不管怎样,如果你在没有括号的情况下调用它,什么都不会发生。

    scala> val x = 10
    x: Int = 10
    
    scala> val y = () => println("i'm a function")
    y: () => Unit = <function0>
    
    scala> x
    res0: Int = 10
    
    scala> y
    res1: () => Unit = <function0>
    

    当你有这样的函数定义时:

    def z() = println("i'm a function def")
    

    那么你可以省略括号并调用它。

    scala> def z() = println("i'm a function def")
    z: ()Unit
    
    scala> z
    i'm a function def
    

    在您的情况下,fun 就像一个值定义(它是一个value parameter)。

    发生的情况是,当 Scala 评估您的 for 表达式时,它不会对 fun 执行任何操作。

    就像你在上面的例子中做y vs y()一样。

    【讨论】:

      【解决方案3】:

      我只是想根据上面非常有用的答案和一些额外的研究来分享我总结的信息。可能对其他人也有帮助。 在 Scala 中可以通过两种方式声明函数: - "call-by-name" - 它们在字节码中以简单的方式表示为普通的 Java 函数。 - "call-by-value" - 从 Scala 的抽象角度来看,它们存储在变量中。实际上,在字节码中,它们存储在实现单一抽象方法接口 (SAM) 的类中,并作为方法参数或普通变量传递。

      当我们调用一个不带参数的 "call-by-name" 函数时,无论我们如何使用或不使用括号来调用它,它总是会被执行。 所以现在 myFunctionmyFunction() 是一样的。 当我们调用不带参数的 "call-by-value" 函数时,我们有两种情况: - 当我们后面没有括号时 - myFunction - 我们只是引用指向函数的变量而不执行函数本身。 - 当我们有括号时 - myFunction() - 我们实际上调用(执行)函数。

      声明函数的不同方式:

      def myFunction = 5 * 5
      

      这是按名称调用功能。以这种方式声明,调用时不允许使用括号。所以调用 myFunction() 不会编译。这样称呼它:myFunction

      def myFunction() = 5 * 5
      

      这是按名称调用功能。可以调用带括号和不带括号的函数——效果是一样的。所以 myFunctionmyFunction() 是一样的。

      def myFunction = () => 5 * 5
      

      这是 按值调用 函数(即使声明为 def 而不是 val )。当用括号调用它时 - myFunction() - 执行函数,当不带括号调用它时 - myFunction - 函数不被执行 - 只是提到了 val 持有它。

      类似地,如果我们将函数声明为其他函数的参数:

      def enclosingFunc(myFunction: => Void)
      

      这是按名称调用功能。在封闭函数内部,只有在不带括号的情况下调用它才会执行 - myFunction。带括号 - myFunction() - 不会编译。

      在这种情况下,enclosureFunc 可以这样调用:

      enclosingFunc(n = 1; n +=1)
      
      
      def enclosingFunc(myFunction: () => Int)
      

      这是按值调用函数。我们如何在封闭函数的主体中调用它很重要。如果在没有括号的情况下调用它 - myFunction - 它不会被执行,但我们只是引用它的对象。如果用括号调用它 - myFunction() - 它将被调用并执行。

      在这种情况下 enclosureFunc 只能以这种方式调用:

      enclosingFunc( () => n = 1; n +=1)
      

      【讨论】:

      • 这涉及到很多相关的话题,但我会尽量不要过度思考这一点的本质。 val 在声明时进行评估。 def 在评估表达式中使用时进行评估。使用(...) 调用时,会评估在=&gt; 左侧包含任何内容的函数类型。
      【解决方案4】:

      在第一个示例中,函数fun 没有被调用,它有点像坐在那里。添加括号会导致对传入的函数求值。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-08-25
        • 2018-10-08
        • 1970-01-01
        • 2016-06-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多