【问题标题】:What does "code: => Unit" mean in scala?scala中的“代码:=>单位”是什么意思?
【发布时间】:2011-09-11 22:24:48
【问题描述】:

有人知道scala中=>单元的类型吗?我不知道 => Unit 的含义以及如何使用它。我定义了如下函数:

def test(code: => Unit){
   print("start ...")
   code
   print("end ....")
}

test(print(1))

这是否意味着任何参数返回 Unit 的函数?

谢谢

【问题讨论】:

    标签: scala types


    【解决方案1】:

    这种参数被称为按名称参数

    => B 代表一个代码块,它返回一个B 值,它们的目的是只有在你调用参数时才会计算它们。

    def foo(code: => Int) {
        println("Not yet evaluated")
        val result = code
        println("parameter evaluated %s, is it an int ? %s " format (
               result, result.isInstanceOf[Int]) )
    }
    

    您可以通过以下方式拨打foo

    foo(1) 
    

    val a = 3
    val b = 5
    foo {
      val c = a * a
      c * b
    }
    

    另一种传递参数的方式是按值:参数在发送到方法之前被评估

    def foo(code : Int) {
        println("Parameter already evaluated")
        val result = code
        println("parameter evaluated : " + result)
    }
    

    参考文献

    Extract from the Book Functionnal Programming in Scala

    More differences between by-name parameter and by-value parameter illustrated

    【讨论】:

    • 关于您的调用示例,在传入多行代码块时,将括号放在参数周围更为惯用:foo { ... }
    • 不是一个好的答案,因为它没有区分名称参数和惰性(按需调用)参数。
    • @Dianel 您能否帮助我了解如何增强我的答案,例如一种可以明确区分这两种情况的情况? @Aaron 已编辑
    【解决方案2】:

    x: => Type 中,x 是一个按名称调用 参数。这与采用不带参数的函数的参数不同:x: () => Type

    【讨论】:

    • 特别不同,因为在调用站点,要计算的表达式是直接给出的,而不是作为函数(因此test(print(1))而不是test(() => print(1)))。
    • 感谢有关“不带参数的函数”的部分:我正在寻找有关该语法的解释。当然,这是完全有道理的,所以我自己应该想出来的:)
    【解决方案3】:

    这称为by name 参数,与按名称调用参数评估策略有关。请参阅链接的维基百科文章,了解类似但不相同的参数传递方式。

    为了更好的解释,让我们首先考虑两种最常见的参数评估策略:按值调用和按引用调用。

    按值调用是迄今为止最常见的评估策略。例如,它是 Java 中唯一的策略,也是 C 中的默认策略。例如,考虑这个简单的 Java 程序:

    public class ByValue {
        static public void inc(int y) {
            y++;
        }
    
        static public void main(String... args) {
            int x = 0;
            inc(x);
            System.out.println(x);
        }
    }
    

    它将打印0,因为x 的值被复制到y,所以当y 增加时它不会改变x 中的原始值。将此与此 C++ 程序与按引用调用的程序进行对比:

    #include <stdio.h>
    
    void inc(int &y) {
        y++;
    }
    
    int main() {
        int x = 0;
        inc(x);
        printf("%d\n", x);
    }
    

    这将打印1,因为对x的引用被传递给inc,而不是x的值。

    请注意,Java 按值传递对象引用,这导致一些人声称它确实按引用调用。这是不正确的,如果您将 new 对象分配给函数的参数,它不会反映在函数的调用者中。

    那么,按名称调用是什么样的?在按名称调用中,既不传递值也不传递引用。相反,整个代码被传递,并且在任何使用参数的地方,代码都被执行并使用它的结果。例如:

    object ByName {
      def incIfZero(y: => Int): Int = if (y == 0) y + 1 else y
    
      def main(args: Array[String]) {
        var x = 0
        x = incIfZero( { val tmp = x; x += 1; tmp } )
        println(x)
      }
    }
    

    这个例子打印2而不是1,因为作为参数传递的代码块被计算了两次。执行的时候好像main的第二行是这样写的:

    x = if ({ val tmp = x; x += 1; tmp }) { val tmp = x; x += 1; tmp } + 1 else { val tmp = x; x += 1; tmp }
    

    现在,按名称参数至少有三个有趣的用途:

    1. 它可以用来延迟执行某些事情,直到适当的时间。
    2. 它可用于在某些情况下避免执行。
    3. 它可以用来多次执行某个代码块。

    我认为,第一个和最后一个案例非常明显。这是第二种情况的示例:

    implicit def fromBoolean(b: Boolean) = new { 
      def and(that: => Boolean): Boolean = if (b) that else b }
    val x = 0
    (x > 0) and (10 / x > 0)
    

    如果that 不是按名称参数,则会在最后一行抛出异常。事实上,它只会返回false

    【讨论】:

    • @ashy_32bit 也许吧。我认为人们往往会误解按名称的参数,将它们与函数混淆,然后因为它们并不痛苦这一事实而跌跌撞撞。
    • @DanielC Sobral:深度回答真棒。它甚至比“Scala 中的函数式编程原理” Coursera 课程中的那一个还要好。可能值得建立一个包含在线 REPL 的页面,让人们“实验”代码以更具体地“体验”差异。测试中的解释足够抽象,概念很难理解,特别是如果一个人多年来广泛使用 Java。
    【解决方案4】:

    它的意思是call-by-name,这基本上意味着在你的函数中使用时计算该值。与 Java(以及具有常规类型语法的 Scala)中默认的按值调用相反,在调用方法之前计算值。我猜,单位类型在按值调用中没有多大意义..

    这通常用于创建充当自定义控制结构的函数,或者像您的示例中那样的计时或日志记录。在 Java 中你经常使用 AOP 做的事情。

    【讨论】:

      猜你喜欢
      • 2022-08-11
      • 2011-03-08
      • 2023-03-27
      • 1970-01-01
      • 1970-01-01
      • 2014-01-16
      • 1970-01-01
      • 2010-12-12
      相关资源
      最近更新 更多