Scala中是否直接支持在参数列表中使用元组解包?

10

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

?

的翻译是:


如果你只能有一个方法定义,那种模式匹配就不是很有用了。你需要像Haskell一样有多个定义才能使它变得真正有用,并且这将大大改变语言的结构。 - Landei
为了澄清@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
@James Iry:是的,我知道在Haskell中技术上的“多重定义”只是隐藏的case ... of语句。但从用户的角度来看,这只是一个实现细节。 - Landei
@Landei 将case表达式转换成实现细节。但是它们不是多个定义的概念并不是实现细节。因为f只有一个类型,所有f的模式都必须与f的一个类型相符,所以这一点很明显。 - James Iry
@James Iry:虽然我认为从上下文中很清楚我们不是在谈论重载函数,但我同意称其为“多个定义”是不严谨的。但是有什么更好的术语呢?像“基于模式匹配的分派”这样的东西吗?但那听起来很荒谬。 - Landei
4个回答

11

你要找的功能叫做解构,它的一般形式远不止元组拆包。我经常希望Scala有这个功能,因为它是模式匹配语法的自然扩展:

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

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

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

不幸的是,有一些关于这样的定义存在一些类型安全问题,我认为这使得在参数列表中看到解构的可能性很小。


感谢你们所有人的回答。Daniel 的回答也是正确的,但我选择了这个答案,因为它包含了有用的网络参考资料,可以更好地理解一些技术细节。再次感谢大家! - letmaik
不错,我给你点赞——解构需要一行谓词代码,但仍然比我的Tuple._0等更容易查看。 - doug

4
你可以创建一个函数,接收与元组类型相对应的参数列表,将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...) 在这种情况下,我不能只改变函数定义。 - letmaik
你可以使用函数柯里化,将函数的某些部分的参数应用“tupled”。然而,这总是比使用中间变量如“val(a,b)= tuple”更复杂。尽管如此,我必须说,我并不讨厌tuple._1或tuple._2。 - JaimeJorge

2

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

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

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

2
不是多重分派。Haskell甚至没有单一的动态分派。 - James Iry
@James 我相信你知道你在说什么,但是在我看来,它似乎根据参数的运行时值选择要应用的定义,这正是多重分派的定义。那么有什么区别呢? - Daniel C. Sobral
如果你再看一遍他的例子,就会发现没有分派 - 他并没有试图动态选择不同的方法。他所做的只是将元组拆分成命名变量。他只是碰巧使用了名称“x”来展示Scala可能在解构方法参数上添加的一些语法糖的示例。 - James Iry
@James 嗯,是的,但我假设那只是他实际问题的简化。 - Daniel C. Sobral

0
另一种方法是将参数列表和元组视为语言中的同一事物。

我记得Martin Odersky说过,在未来的Scala版本中,参数列表和元组可能会被统一起来。 - Kipton Barros
是的,他提到过。但我不会抱太大希望,因为方法重载存在一些非常棘手的兼容性问题。 - Jesper Nordenberg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接