Scala:从元组数组/ RDD中获取第n个元素的总和

Moh*_*itt 5 collections functional-programming scala

我有一个tuple像这样的数组:

val a = Array((1,2,3), (2,3,4))
Run Code Online (Sandbox Code Playgroud)

我想为下面的方法编写泛型方法:

def sum2nd(aa: Array[(Int, Int, Int)]) = {
      aa.map { a => a._2 }.sum
      }
Run Code Online (Sandbox Code Playgroud)

所以我正在寻找一种方法,如:

def sumNth(aa: Array[(Int, Int, Int)], n: Int)
Run Code Online (Sandbox Code Playgroud)

Tra*_*own 9

有几种方法可以解决这个问题.最简单的是使用productElement:

def unsafeSumNth[P <: Product](xs: Seq[P], n: Int): Int =
  xs.map(_.productElement(n).asInstanceOf[Int]).sum
Run Code Online (Sandbox Code Playgroud)

然后(注意索引从零开始,所以n = 1给我们第二个元素):

scala> val a = Array((1, 2, 3), (2, 3, 4))
a: Array[(Int, Int, Int)] = Array((1,2,3), (2,3,4))

scala> unsafeSumNth(a, 1)
res0: Int = 5
Run Code Online (Sandbox Code Playgroud)

但是,这种实现可能会在运行时以两种不同的方式崩溃:

scala> unsafeSumNth(List((1, 2), (2, 3)), 3)
java.lang.IndexOutOfBoundsException: 3
  at ...

scala> unsafeSumNth(List((1, "a"), (2, "b")), 1)
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
  at ...
Run Code Online (Sandbox Code Playgroud)

即,如果元组没有足够的元素,或者你要求的元素不是元素Int.

您可以编写一个在运行时不会崩溃的版本:

import scala.util.Try

def saferSumNth[P <: Product](xs: Seq[P], n: Int): Try[Int] = Try(
  xs.map(_.productElement(n).asInstanceOf[Int]).sum
)
Run Code Online (Sandbox Code Playgroud)

然后:

scala> saferSumNth(a, 1)
res4: scala.util.Try[Int] = Success(5)

scala> saferSumNth(List((1, 2), (2, 3)), 3)
res5: scala.util.Try[Int] = Failure(java.lang.IndexOutOfBoundsException: 3)

scala> saferSumNth(List((1, "a"), (2, "b")), 1)
res6: scala.util.Try[Int] = Failure(java.lang.ClassCastException: ...
Run Code Online (Sandbox Code Playgroud)

这是一种改进,因为它迫使呼叫者解决失败的可能性,但它也有点烦人,因为它迫使呼叫者解决失败的可能性.

如果你愿意使用Shapeless,你可以拥有两全其美:

import shapeless._, shapeless.ops.tuple.At

def sumNth[P <: Product](xs: Seq[P], n: Nat)(implicit
  atN: At.Aux[P, n.N, Int]
): Int = xs.map(p => atN(p)).sum
Run Code Online (Sandbox Code Playgroud)

然后:

scala> sumNth(a, 1)
res7: Int = 5
Run Code Online (Sandbox Code Playgroud)

但坏的甚至没有编译:

scala> sumNth(List((1, 2), (2, 3)), 3)
<console>:17: error: could not find implicit value for parameter atN: ...
Run Code Online (Sandbox Code Playgroud)

但这仍然不完美,因为它意味着第二个参数必须是一个字面数字(因为它需要在编译时知道):

scala> val x = 1
x: Int = 1

scala> sumNth(a, x)
<console>:19: error: Expression x does not evaluate to a non-negative Int literal
       sumNth(a, x)
                 ^
Run Code Online (Sandbox Code Playgroud)

但在许多情况下,这不是问题.

总结一下:如果您愿意承担合理的代码崩溃程序的责任,请使用productElement.如果你想多一点安全性(在有些不便的费用),使用productElementTry.如果您想要编译时安全(但有一些限制),请使用Shapeless.