【问题标题】:What is the point of multiple parameter clauses in function definitions in Scala?Scala 函数定义中的多个参数子句有什么意义?
【发布时间】:2011-11-08 21:47:54
【问题描述】:

我试图理解多参数子句的这种语言特性的意义以及你为什么要使用它。 例如,这两个函数到底有什么区别?

class WTF {
    def TwoParamClauses(x : Int)(y: Int) = x + y
    def OneParamClause(x: Int, y : Int) = x + y
}

>> val underTest = new WTF
>> underTest.TwoParamClauses(1)(1) // result is '2'
>> underTest.OneParamClause(1,1) // result is '2' 

Scala specification at point 4.6 中有相关内容。看看这对你是否有意义。

注意:规范称这些为“参数子句”,但我认为有些人也可能称它们为“参数列表”。

【问题讨论】:

  • 这里是非 scala 程序员。也许只是风格不同?并非所有事情都必须只有一种方法。
  • 好的,Daniel Sobral 对stackoverflow.com/questions/4697404/…987654322@的回答中有一些答案
  • 这里接受的答案可能回答了这个问题:(1)所以您不必在第二个子句中的第一个参数子句中指定类型; (2) 库设计的灵活性; (3) 让currying更容易:stackoverflow.com/questions/4915027/…
  • 好吧,这真的困扰我帖子中没有明确的函数——只有方法。函数是一等值,而方法不是。方法是对象的“一部分”,是对象“知道如何响应”的东西。函数是具有应用方法的对象(FunctionN/PartialFunctionN 类型)——应用方法不是函数。记住这一点很重要,因为方法和函数的规则不同。例如,函数不能重载,方法(没有隐式转换为函数)不能作为函数传递。
  • @pst,虽然我同意这种观点,但我惊讶地发现Scala Language Spec(第 4.6 节,函数声明)将方法称为特定类型的函数。也许,准确地说,最好区分方法函数对象

标签: scala functional-programming currying


【解决方案1】:

这里是多参数列表的三种实际用途,

  1. 帮助类型推断。这在使用高阶方法时特别有用。下面,g2的类型参数A是从第一个参数x推断出来的,所以第二个参数f中的函数实参可以省略,

    def g1[A](x: A, f: A => A) = f(x)
    g1(2, x => x) // error: missing parameter type for argument x
    
    def g2[A](x: A)(f: A => A) = f(x)
    g2(2) {x => x} // type is inferred; also, a nice syntax
    
  2. 对于隐式参数。只有最后一个参数列表可以标记为隐式,单个参数列表不能混合隐式和非隐式参数。下面g3的定义需要两个参数列表,

    // analogous to a context bound: g3[A : Ordering](x: A)
    def g3[A](x: A)(implicit ev: Ordering[A]) {}
    
  3. 根据之前的参数设置默认值,

    def g4(x: Int, y: Int = 2*x) {} // error: not found value x
    def g5(x: Int)(y: Int = 2*x) {} // OK
    

【讨论】:

    【解决方案2】:

    TwoParamClause 涉及两次方法调用,而OneParamClause 仅调用一次 function 方法。我认为您正在寻找的术语是currying。在众多用例中,它可以帮助您将计算分解为小步骤。这个answer 可能会让你相信柯里化的用处。

    【讨论】:

    • 带有两个参数子句的表单只调用一次 method -- 该调用的结果是一个 function 然后应用它导致最终值。
    • @pst:但是应用一个函数也会调用一个方法(即函数的apply方法)。
    • @Alexey Romanov 是的。最初的帖子措辞不同;-) 在现在过时的评论中,我试图强调方法(对象知道如何“响应”)和函数(可以应用的离散对象,甚至如果此类应用程序是对其调用方法的结果)。我仍然不同意术语“...只调用一次函数”,因为它只调用了一次 OneParamClause 方法
    • 我希望它现在在技术上是正确的。很抱歉有任何混淆。 This博客很好地涵盖了函数和方法的区别
    【解决方案3】:

    两个版本在类型推断方面存在差异。考虑

    def f[A](a:A, aa:A) = null
    f("x",1)
    //Null = null
    

    这里,A 类型绑定到Any,这是StringInt 的超类型。但是:

    def g[A](a:A)(aa:A) = null
    g("x")(1)
    
    error: type mismatch;
     found   : Int(1)
     required: java.lang.String
           g("x")(1)
                  ^
    

    如您所见,类型检查器只考虑第一个参数列表,因此A 绑定到String,因此第二个参数列表中aaInt 值是类型错误。

    【讨论】:

      【解决方案4】:

      多个参数列表可以帮助 scala 类型推断更多详细信息请参阅:Making the most of Scala's (extremely limited) type inference

      类型信息不会从左到右参数列表中,只会从从左到右跨越参数列表。因此,即使 Scala 知道前两个参数的类型……这些信息也不会流向我们的匿名函数。

      ...

      现在我们的二元函数在一个单独的参数列表中,先前参数列表中的任何类型信息都用于填充我们函数的类型 ...因此我们不需要注释 lambda 的参数。

      【讨论】:

      • 请确保在帖子中包含摘录和/或推理,而不仅仅是裸链接。然而,一个非常好的文章发现。
      • 我认为我的简短描述足以引起人们的注意,为了获得完整的参考,应该阅读博客。但是这样也很好:)
      【解决方案5】:

      在某些情况下,这种区别很重要:

      1. 多个参数列表允许您拥有类似 TwoParamClauses(2);这是自动生成的 Int => Int 类型的函数,它将 2 添加到其参数。当然,您也可以使用 OneParamClause 自己定义相同的内容,但需要更多的击键次数

      2. 1234563李>

      除此之外,我认为,不同之处在于风格。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多