我有一个tuple数组,如下所示:
tuple
val a = Array((1,2,3), (2,3,4))
我想为下面的方法写一个泛型方法:
def sum2nd(aa: Array[(Int, Int, Int)]) = { aa.map { a => a._2 }.sum }
所以我正在寻找一种方法,如:
def sumNth(aa: Array[(Int, Int, Int)], n: Int)
5fjcxozz1#
有几种方法可以解决这个问题。最简单的方法是使用productElement:
productElement
def unsafeSumNth[P <: Product](xs: Seq[P], n: Int): Int = xs.map(_.productElement(n).asInstanceOf[Int]).sum
然后(注意索引从零开始,所以n = 1给了我们第二个元素):
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
但是,这种实现可能会以两种不同的方式在运行时崩溃:
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。你可以编写一个在运行时不会崩溃的版本:
Int
import scala.util.Try def saferSumNth[P <: Product](xs: Seq[P], n: Int): Try[Int] = Try( xs.map(_.productElement(n).asInstanceOf[Int]).sum )
然后:
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: ...
这是一种改进,因为它迫使调用者解决失败的可能性,但它也有点烦人,因为它迫使调用者解决失败的可能性。如果你愿意使用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
scala> sumNth(a, 1) res7: Int = 5
但坏的甚至不能编译:
scala> sumNth(List((1, 2), (2, 3)), 3) <console>:17: error: could not find implicit value for parameter atN: ...
但这仍然不是完美的,因为这意味着第二个参数必须是一个字面量(因为它需要在编译时知道):
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) ^
不过,在很多情况下,这并不是问题。总而言之:如果你愿意为合理的代码崩溃负责,请使用productElement。如果你想更安全一点(以一些不便为代价),可以将productElement与Try一起使用。如果你想要编译时安全(但有一些限制),请使用Shapeless。
Try
8i9zcol22#
Antoher不使用shapeless的类型安全方法是提供一个函数来提取你需要的元素:
shapeless
def sumNth[T, E: Numeric](array: Array[T])(extract: T => E) = array.map(extract).sum
然后你可以这样定义sum2nd:
sum2nd
def sum2nd(array: Array[(Int, Int, Int)]): Int = sumNth(array)(_._2)
或者像这样:
val sum2nd: Array[(Int, Int, Int)] => Int = sumNth(_)(_._2)
eimct9ow3#
你可以这样做,虽然它不是真正的类型安全:
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
fcg9iug34#
从Scala 3开始,元组的元素可以通过类似函数调用的语法(例如,t(1)得到元组t的第二个元素)。sumNth方法可以这样实现:
t(1)
t
sumNth
def sumNth(arr: Array[(Int, Int, Int)], n: Int) = arr.map(_(n).asInstanceOf[Int]).sum
Scastie Demo
4条答案
按热度按时间5fjcxozz1#
有几种方法可以解决这个问题。最简单的方法是使用
productElement
:然后(注意索引从零开始,所以
n = 1
给了我们第二个元素):但是,这种实现可能会以两种不同的方式在运行时崩溃:
也就是说,如果元组没有足够的元素,或者你请求的元素不是
Int
。你可以编写一个在运行时不会崩溃的版本:
然后:
这是一种改进,因为它迫使调用者解决失败的可能性,但它也有点烦人,因为它迫使调用者解决失败的可能性。
如果你愿意使用Shapeless,你可以两全其美:
然后:
但坏的甚至不能编译:
但这仍然不是完美的,因为这意味着第二个参数必须是一个字面量(因为它需要在编译时知道):
不过,在很多情况下,这并不是问题。
总而言之:如果你愿意为合理的代码崩溃负责,请使用
productElement
。如果你想更安全一点(以一些不便为代价),可以将productElement
与Try
一起使用。如果你想要编译时安全(但有一些限制),请使用Shapeless。8i9zcol22#
Antoher不使用
shapeless
的类型安全方法是提供一个函数来提取你需要的元素:然后你可以这样定义
sum2nd
:或者像这样:
eimct9ow3#
你可以这样做,虽然它不是真正的类型安全:
fcg9iug34#
从Scala 3开始,元组的元素可以通过类似函数调用的语法(例如,
t(1)
得到元组t
的第二个元素)。sumNth
方法可以这样实现:Scastie Demo