为什么scala不能在元组上隐式匹配?

时间:2013-12-06 20:22:35

标签: scala

你可以在ruby中执行以下操作:

l = [[1, 2], [3, 4], [5, 6]]
m  = l.map {|(a, b)| a+b}

但你不能在scala中执行以下操作:

val a = List((1, 2), (3, 4), (5, 6))

a.map((f, s) => f + s)
<console>:9: error: wrong number of parameters; expected = 1
a.map((f, s) => f + s)

相反,你必须这样做:

a.map { case (f, s) => f + s }

我发现这个相当罗嗦,因为scala定义了一个“元组”类型,我期待它也能在它之上提供语法糖,以便像上面那样隐式匹配。是否有一些深层原因导致不支持这种匹配?有更优雅的方式吗?

4 个答案:

答案 0 :(得分:5)

原因

原因是您尝试使用的语法已经具有意义。当高阶函数需要双参数函数时使用它。例如,使用reducefold

List(1,2,3).reduce((a,b) => a+b)

解决方案

可以通过定义自己的隐式方法来实现更清洁的方法:

import scala.collection.generic.CanBuildFrom
import scala.collection.GenTraversableLike

implicit class EnrichedWithMapt2[A, B, Repr](val
self: GenTraversableLike[(A, B), Repr]) extends AnyVal {
  def mapt[R, That](f: (A, B) => R)(implicit bf: CanBuildFrom[Repr, R, That]) = {
    self.map(x => f(x._1, x._2))
  }
}

然后你可以这样做:

val a = List((1, 2), (3, 4), (5, 6))
a.mapt((f, s) => f + s)  // List(3, 7, 11)

其他替代方案

你可以做一些其他技巧,比如使用tupled,但它们并没有真正帮助你解决你描述的情况:

val g = (f: Int, s: Int) => f + s
a.map(g.tupled)

或者只是

a.map(((f: Int, s: Int) => f + s).tupled)

答案 1 :(得分:4)

更多替代方案

如果您有更多时间,您甚至可以使用含义执行以下操作:

val _1 = { t:  { def _1: Int } => t._1 }
val _2 = { t:  { def _2: Int } => t._2 }

implicit class HighLevelPlus[A](t: A => Int) {
  def +(other: A => Int) = { a: A => t(a) + other(a) }
  def *(other: A => Int) = { a: A => t(a) * other(a) }
}

val a = List((1, 2), (3, 4), (5, 6))
a map _1 + _2

monad和关键字foryield的另一种可能性:

val a = List((1, 2), (3, 4), (5, 6))
for((i, j) <- a) yield i + j

但这可能不是你喜欢的解决方案。

答案 2 :(得分:2)

  

是否有一些深层原因导致不支持这种匹配?

是的,因为在Scala中,语法

(f, s) =>

表示带有2个参数的匿名函数。

  

有更优雅的方式吗?

(有些诙谐的回答。)使用Haskell,其中\(f, s) ->实际上是指以元组为参数的函数。

答案 3 :(得分:0)

您可以使用_1的{​​{1}},_2属性获得所需的效果

Tuple