【问题标题】:Scala collections: why do we need a case statement to extract values tuples in higher order functions?Scala 集合:为什么我们需要一个 case 语句来提取高阶函数中的值元组?
【发布时间】:2015-02-14 13:38:34
【问题描述】:

Tuple Unpacking in Map Operations 相关,我不明白为什么我们需要case(在我看来像是一个偏函数)来从元组中提取值,就像这样:

arrayOfTuples map {case (e1, e2) => e1.toString + e2}

而不是以与foldLeft 中相同的方式进行提取,例如

def sum(list: List[Int]): Int = list.foldLeft(0)((r,c) => r+c)

反正第一种情况我们不指定参数的类型,那为什么还要case语句呢?

【问题讨论】:

    标签: scala


    【解决方案1】:

    带有一个原始参数的 Lambda

    var listOfInt=(1 to 100).toList
    listOfInt.foldRight(0)((current,acc)=>current+acc)
    

    你有一个对两个参数进行操作的 lambda 函数。

    具有一个元组类型参数的 Lambda

    var listOfTuple=List((1,"a"),(2,"b"),(3," "))
    listOfTuple.map(x => x._1.toString + x._2.toString)
    

    你有一个 lambda 函数处理一个参数(Tuple2[Int, String] 类型)

    两者都适用于类型推断。

    带有一个参数的部分 lambda

    listOfTuple.map{case (x,y) => x.toString + y.toString}
    

    你有一个 lambda 函数,使用一个参数(Tuple2[Int, String] 类型)。然后,此 lambda 函数在内部使用 Tuple2.unapply 将一个参数分解为多个值。这仍然适用于类型推断。值的分解(“模式匹配”)需要case

    这个例子有点不直观,因为 unapply 返回一个 Tuple 作为它的结果。在这种特殊情况下,可能确实存在技巧,因此 Scala 直接使用提供的元组。但是我真的不知道有这样的把戏。

    更新:带有柯里化的 Lambda 函数

    确实有诀窍。与

    import Function.tupled
    listOfTuple map tupled{(x,y) => x.toString + y.toString}
    

    您可以直接使用元组。但这当然是一个技巧:您提供了一个对两个参数而不是元组进行操作的函数。 tupled 然后获取该函数并将其更改为不同的函数,对元组进行操作。这种技术也称为uncurrying

    备注:

    y 已经是一个字符串时,y.toString 是多余的。这不被认为是好的风格。为了示例,我将其保留。您应该在实际代码中省略它。

    【讨论】:

    • 关于y.toString - 我只是拿了第一个foldLeft,我可以在谷歌找到,我们项目中的例子太复杂了。
    • 太棒了!我不明白为什么 listOfTuple.map{case (x,y) => x.toString + y.toString} 被称为部分 lambda。在我看来,我们已经使用了所有参数。
    【解决方案2】:

    因为在 Scala 中,函数参数列表和元组不像在 Haskell 和其他函数式语言中那样是一个统一的概念。所以一个函数:

    (t: (Int, Int)) => ...
    

    和函数不是一回事:

    (e1: Int, e2: Int) => ...
    

    在第一种情况下,您可以使用模式匹配来提取元组元素,并且始终使用case 语法来完成。实际上,表达式:

    {case (e1, e2) => ...}
    

    是以下的简写:

    t => t match {case (e1, e2) => ...}
    

    有一些关于统一元组和函数参数列表的讨论,但在 Java 重载规则以及默认/命名参数方面存在一些问题。所以,我认为这些概念不太可能在 Scala 中统一。

    【讨论】:

    • 我不认为这是正确的。即使在 Haskell 中,带有两个参数 f :: a -> b -> c 的函数 f 也不同于带有 2 元组作为其唯一参数 g :: (a,b) -> c 的函数 g。您可以使用 currying 在这两个函数之间进行转换:f = curry g。 Haskell 的优点是,您可以直接在参数声明中进行模式匹配。但这适用于任何类型的构造函数,不仅适用于元组:h (Just x) = x + 1。在 Scala 中,您必须显式调用模式匹配(使用“match”或“case”)。
    • 你把事情搞混了。 Haskell 函数f :: a -> b -> c 对应于Scala 函数f: A => B => C 或方法def f(x: A)(y: B): C。 Haskell 函数g :: (a, b) -> c 对应于Scala 函数g: ((A, B)) => C 或方法def g(t: (A, B)): C。 Scala 函数 h: (A, B) => C 或方法 def h(x: A, y: B): C 不能用一个参数调用,因此在 Haskell 中没有对应关系。但是你是正确的,Haskell 可以在参数列表中进行模式匹配,而 Scala 不能。
    • Dotty(最终将成为 Scala 3)现在自动对函数参数进行元组处理。
    猜你喜欢
    • 1970-01-01
    • 2011-02-12
    • 2017-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-17
    相关资源
    最近更新 更多