【问题标题】:scala function literal and methods and underscorescala函数文字和方法和下划线
【发布时间】:2017-09-19 21:27:30
【问题描述】:

我有一段非常简单的代码,我无法掌握。我正在阅读函数文字和方法。我在 repl 中这样做。

scala> val v = (a:Int, b:Int, c:Int) => {a+b+c}
v: (Int, Int, Int) => Int = $$Lambda$1368/1731643198@4ae730ca

scala> val v1 = v(1,2,_:Int)
v1: Int => Int = $$Lambda$1369/903167315@93d6f4

scala> v1 (10)
res29: Int = 13

scala>

scala> val v2 = v _
v2: () => (Int, Int, Int) => Int = $$Lambda$1370/5485285@7dd5d17a

scala> val v3 = v2()
v3: (Int, Int, Int) => Int = $$Lambda$1368/1731643198@4ae730ca

scala> val v4 = v3(1,2,3)
v4: Int = 6

scala> def sumMe(a:Int, b:Int, c:Int) = { a+b+c}
sumMe: (a: Int, b: Int, c: Int)Int

scala> val v7 = sumMe _
v7: (Int, Int, Int) => Int = $$Lambda$1371/906350712@6c86938f

scala> v7(1,2,3)
res30: Int = 6

我需要一些帮助来理解上面发生的事情。我将从代码的底部开始。当我创建方法 sumMe 并将其分配给右侧带有“_”的“v7”时,我知道我没有执行该方法。 val v7= sumMe_ 的输出对我来说很清楚,因为它只是告诉我 v7 将采用 3 个参数,并返回一个 int。目前感觉还可以。

现在当我转到我的 `val v1 = v(1,2,_:Int) 时,我仍然可以关联到它将创建一个函数对象并分配给 v1,实际上我使用的是 Scala s Function1 的 apply 方法是我的看法。

我希望到目前为止我理解它。如果我上面的理解是正确的,那么引起最大困惑的是val v2 = v _。根据我所看到的输出,我必须以不同的方式称呼这个东西。基本上我无法理解为什么 v2 与 v7 不同。 v2 不接受任何参数,并给了我一个可以调用的函数。如果我定义为val v = ... 的那种函数文字总是如此,那么当我执行val v1 = v(1,2,:_Int) 时,为什么不从类似于v2's case 的scala v1:()=>Int=>Int 得到它。

最后,为什么v7=sumMe _ 不能给我与val v2 = v_ 相同的输出

【问题讨论】:

    标签: scala functional-programming


    【解决方案1】:

    在 Scala 中,我们将 方法函数 区分开来。当您定义sumMe 时,您定义的是一个方法,而您的其他声明是函数。方法,在 Scala 中,are non value types,表示方法本身没有价值。当您尝试将其分配给一个值时,有一个名为 eta expansion 的隐式转换将其转换为相应的函数类型。来自规范:

    方法类型不作为值类型存在。如果使用方法名 作为一个值,它的类型被隐式转换为对应的 函数类型。

    现在我们已经掌握了方法和函数的知识,让我们来分析发生了什么。

    当我创建方法sumMe并将其分配给“v7”时,右侧带有“_”,我知道我没有执行该方法

    没错。当您执行 sumMe _ 时,您正在使用 eta-expansion 将方法转换为函数。

    现在,当我转到我的 val v1 = v(1, 2, _: Int) 时,我仍然可以将其关联起来 它将创建一个函数对象并分配给 v1

    再一次,你是对的。 v1 现在是 Function1[Int, Int] 类型的部分应用函数。

    基本上我无法理解为什么 v2 与 v7 不同。

    v2 是什么?它是通过部分应用现有函数对象而创建的函数对象。由于这个Function3[Int, Int, Int, Int] 类型的函数已经固定在它的参数列表中,部分应用它只会将它嵌套在另一个函数对象中,现在类型为Function0,使其成为Function0[Function3[Int, Int, Int, Int]]

    【讨论】:

    • 您好 Yuval,您的解释很有帮助。一旦我一遍又一遍地阅读你的帖子,我会回来问你更多关于它的问题。同时,您能否尝试解释一下答案结尾的含义-注意后者是特殊的....您是说我的代码中有构建错误吗?
    • @curiousengineer 我删除了该部分,因为声明范围似乎存在问题。您可以对类的成员执行此操作,但不能在方法声明中执行此操作,我正在努力找出确切的规则,但它们对于答案并不重要。
    【解决方案2】:

    在 Scala 中,函数是值,这意味着您可以在变量中分配任何函数。每当您在 def 前面应用 placeholder("_") 时,它都会将 def 转换为具有相同输入和输出类型的函数。如果在值前面应用占位符,它将转换为以单位为输入并以输出为返回值的函数**[() => T]**。例如:

     scala> val a = 2
     a: Int = 2
    
    scala> val fun1  = a _
    fun1: () => Int = <function0>
    
    scala> def sum(a:Int, b:Int) = a+ b
    sum: (a: Int, b: Int)Int
    
    scala> val fun2 = sum _
    fun2: (Int, Int) => Int = <function2>
    

    当您尝试在“def”中传递部分输入参数时,它将返回 部分应用 函数。例如:

    scala> val fun3 = sum(1,_:Int)
    fun3: Int => Int = <function1>
    

    fun3 被称为偏应用函数。

    【讨论】:

      【解决方案3】:

      functionmethod 有一些事情需要清除,

      1. 函数:我们使用valvar来定义function,通常是Anonymous Function,在Scala中,有@987654328 @, Function1, Function2 ... 对于这些Anonymous Function,函数类型如:(T0, T1...TN) =&gt; U

        所以 function v 实际上是一个带有 3 个参数的Function3

      2. 方法:用于defmethod主体声明,参数return type

      对于val v2 = v _实际上等于val v2 = () =&gt; v,其中通配符_将扩展为() =&gt; v,而v2表示它是一个创建另一个函数的函数(v),没有参数。所以@987654344 @ 表示invoke v2() 创建v 函数,所以本质上v3 等于v

      对于 val v7 = sumMe _ 这意味着 convert method sumMe 到一个函数,在那里通配符 _ 将扩展为 (a: Int, b: Int, c: Int) =&gt; sumMe(a, b, c) ,所以 sumMe _ 将创建一个基本上相等的新函数到v,如果您使用v7 _,它也会创建相同的function,如val v2 = v _

      参考

      Difference between method and function in Scala

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-06-15
        • 1970-01-01
        • 2018-11-21
        • 2011-10-19
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多