【问题标题】:Will tuple unpacking be directly supported in parameter lists in Scala?Scala 的参数列表会直接支持元组解包吗?
【发布时间】:2011-06-19 00:40:18
【问题描述】:

在 Haskell 中你可以写:

x :: (Int,Int) -> Int
x (p,s) = p

在 Scala 中你会这样写:

def x(a: (Int, Int)) = a._1

或:

def x(a: (Int, Int)) = a match {
    case (p, s) => p
}

为什么没有类似的东西

def x(_: (p: Int, s: Int)) = p

def x(foo: (p @ Int, s @ Int)) = p

?

【问题讨论】:

  • 如果你只能有 one 方法定义,那么这种模式匹配不会很有用。您需要像 Haskell 中这样的多个定义才能使其真正有用,而这会极大地改变语言的结构。
  • 为了澄清@Landei 的评论,Haskell 不允许函数的多个定义。它只是为模式匹配提供了很好的糖。在 Scala 术语中,就好像你可以写 "def f[A](Some(p : A), alt : A) = p;def f(None, alt : A) = alt" 而不是 "def f(x : Option[A], alt : A) = x match { case Some(p) => p; case None => alt}"。不幸的是,Scala 具有 Java 风格的重载,并通过子类型化处理代数数据类型,这两者都会产生歧义。
  • @James Iry:是的,我知道从技术上讲,Haskell 中的“多重定义”只是隐藏了 case ... of 语句。但从用户的角度来看,这只是一个实现细节。
  • @Landei 转换成 case 表达式是一个实现细节。但是它们不是多重定义的概念并不是实现细节。鉴于 f 只有一种类型,并且 f 的所有模式必须与 f 的一种类型一致,这在用户面前。
  • @James Iry:虽然我认为从上下文中可以清楚地看出我们不是在谈论重载函数,但我同意称其为“多重定义”是草率的。但更好的术语是什么?诸如“基于模式匹配的调度”之类的东西?但这听起来很荒谬。

标签: scala parameters tuples iterable-unpacking


【解决方案1】:

您正在寻找的功能称为解构,它的一般形式远远超出了元组解包的范围。我经常发现自己希望 Scala 拥有它,因为它是模式匹配语法的自然扩展:

def first((f: Int, l: Int)) = f
def displayName(Person(first, last)) = last + ", " + first

解构(有点)以变量/值定义的形式存在:

val (f, l) = tuple
val Person(first, last) = person

不幸的是,有一些 type safety issues 围绕着这样的定义,我认为你不太可能很快在参数列表中看到解构。

【讨论】:

  • 感谢您的所有回答。 Daniel 的回答也是正确的,但我选择了这个,因为它包含有用的网络参考资料,可以帮助您了解更多技术细节。再次感谢大家!
  • 不错,来自我的 +1 -- 解构需要谓词行代码,但仍然比 myTuple._0 等更容易查看。
【解决方案2】:

您可以创建一个函数来接收对应于元组类型的参数列表,将Function.tupled 应用于该函数,然后应用元组:

scala> def fun(x:Int,y:Int)=x+y
fun: (x: Int,y: Int)Int

scala> val tuple = (1,2)
tuple: (Int, Int) = (1,2)

scala> Function.tupled(fun _)(tuple)
res9: Int = 3

这样您就可以为您的问题找到有效的解决方法

【讨论】:

  • 好吧,关于我举的简单案例,你是对的,但是更复杂的参数呢,比如:list.foldLeft((0,0))((a: (Int, Int ), x: Int) => ... a._1...) 在这里我不能只更改函数定义。
  • 您可以使用函数柯里化,应用tupled 函数某些部分的参数。但是,它总是比使用诸如val (a,b) = tuple 之类的中间变量更复杂。不过,我必须说,我并不讨厌 tuple._1 或 tuple._2
【解决方案3】:

这叫多分派,JVM不支持。 Scala可以重写一个方法,使显式匹配变得不必要,但这不是优先事项——或者,据我所知,甚至没有计划——这样做。

有趣的是,从某种意义上说,它支持函数,但限制是所有变体必须一起出现。例如:

def x: ((Int, Int)) => Int = {
    case (p, _) => p
}

【讨论】:

  • 不是多次分派。 Haskell 甚至没有单一的动态调度。
  • @James 我相信你知道你在说什么,但在我看来,它确实会根据参数的运行时值选择要应用的定义,这是非常多分派的定义。那么有什么区别呢?
  • 如果您再次查看他的示例,则没有调度 - 他并没有尝试动态选择不同的方法。他所做的只是将一个元组分解为命名变量。他只是碰巧以几种不同的方式使用名称“x”来举例说明 Scala 可以在解构方法参数上添加的糖。
  • @James 嗯,是的,但我认为这只是他实际问题的简化。
【解决方案4】:

另一种方法是在语言中使参数列表和元组相同。

【讨论】:

  • 我记得 Martin Odersky 说过参数列表和元组可能会在未来版本的 Scala 中统一。
  • 是的,他提到过。不过我不会屏住呼吸,方法重载存在一些非常棘手的兼容性问题。
猜你喜欢
  • 2014-08-03
  • 2017-02-23
  • 1970-01-01
  • 1970-01-01
  • 2017-09-06
  • 2012-01-20
  • 1970-01-01
  • 1970-01-01
  • 2012-04-16
相关资源
最近更新 更多