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

Scala:从元组数组/RDD获取第n个元素的和,scala,collections,functional-programming,Scala,Collections,Functional Programming,我有一个元组数组,如下所示: val a = Array((1,2,3), (2,3,4)) def sum2nd(array: Array[(Int, Int, Int)]): Int = sumNth(array)(_._2) val sum2nd: Array[(Int, Int, Int)] => Int = sumNth(_)(_._2) 我想为如下方法编写一个通用方法: def sum2nd(aa: Array[(Int, Int, Int)]) = { aa

我有一个元组数组,如下所示:

val a = Array((1,2,3), (2,3,4))
def sum2nd(array: Array[(Int, Int, Int)]): Int = sumNth(array)(_._2)
val sum2nd: Array[(Int, Int, Int)] => Int = sumNth(_)(_._2)
我想为如下方法编写一个通用方法:

def sum2nd(aa: Array[(Int, Int, Int)]) = {
      aa.map { a => a._2 }.sum
      }
因此,我要寻找的方法如下:

def sumNth(aa: Array[(Int, Int, Int)], n: Int)

您可以这样做,尽管它不是真正的类型安全:

  def sumNth(aa: Array[Product], n: Int)= {
    aa.map { a =>
      a.productElement(n) match {
        case i:Int => i
        case _ => 0
      }
    }.sum
  }

sumNth(Array((1,2,3), (2,3,4)), 2) // 7

有几种方法可以做到这一点。最简单的方法是使用
productElement

def unsafeSumNth[P <: Product](xs: Seq[P], n: Int): Int =
  xs.map(_.productElement(n).asInstanceOf[Int]).sum
此实现在运行时可能以两种不同的方式崩溃,不过:

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 ...
也就是说,如果元组没有足够的元素,或者如果您请求的元素不是
Int

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

import scala.util.Try

def saferSumNth[P <: Product](xs: Seq[P], n: Int): Try[Int] = Try(
  xs.map(_.productElement(n).asInstanceOf[Int]).sum
)
这是一种改进,因为它迫使调用方解决失败的可能性,但也有点烦人,因为它迫使调用方解决失败的可能性

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

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
但坏的甚至不会编译:

scala> sumNth(List((1, 2), (2, 3)), 3)
<console>:17: error: could not find implicit value for parameter atN: ...
scala>sumNth(列表((1,2)、(2,3)),3)
:17:错误:找不到参数atN的隐式值:。。。
但这仍然不是完美的,因为这意味着第二个参数必须是一个文字数字(因为它需要在编译时知道):

scala>val x=1
x:Int=1
scala>sumNth(a,x)
:19:错误:表达式x的计算结果不是非负整数文字
萨姆特(a,x)
^
不过,在很多情况下,这不是问题


总而言之:如果您愿意为合理的代码导致程序崩溃承担责任,请使用
productElement
。如果您想要多一点安全性(以一些不便为代价),请将
productElement
Try
一起使用。如果希望编译时安全(但有一些限制),请使用无形状。

另一种不使用无形状的类型安全方法是提供一个函数来提取所需的元素:

def sumNth[T, E: Numeric](array: Array[T])(extract: T => E) =
  array.map(extract).sum
然后您可以这样定义
sum2nd

val a = Array((1,2,3), (2,3,4))
def sum2nd(array: Array[(Int, Int, Int)]): Int = sumNth(array)(_._2)
val sum2nd: Array[(Int, Int, Int)] => Int = sumNth(_)(_._2)
或者像这样:

val a = Array((1,2,3), (2,3,4))
def sum2nd(array: Array[(Int, Int, Int)]): Int = sumNth(array)(_._2)
val sum2nd: Array[(Int, Int, Int)] => Int = sumNth(_)(_._2)

元组元素总是相同的类型吗?为了简单起见,让我们假设相同的类型和相同大小的元组如果您想要一个索引序列(这似乎是您想要的),请使用一个索引序列的数据结构!(例如列表、数组、向量)。如果要访问变量
n
的元素
n
,则元组是错误的选择。如果忽略OP请求特定签名/用法的事实,这是一个合理的解决方案。:)