There are a few ways you can go about this. The simplest is to use productElement
:
有几种方法可以做到这一点。最简单的方法是使用ProductElement:
def unsafeSumNth[P <: Product](xs: Seq[P], n: Int): Int =
xs.map(_.productElement(n).asInstanceOf[Int]).sum
And then (note that indexing starts at zero, so n = 1
gives us the second element):
然后(请注意,索引从零开始,因此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
This implementation can crash at runtime in two different ways, though:
不过,此实现在运行时可能会以两种不同的方式崩溃:
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 ...
I.e., if the tuple doesn't have enough elements, or if the element you're asking for isn't an Int
.
也就是说,如果元组没有足够的元素,或者如果您请求的元素不是Int。
You can write a version that doesn't crash at runtime:
您可以编写一个不会在运行时崩溃的版本:
import scala.util.Try
def saferSumNth[P <: Product](xs: Seq[P], n: Int): Try[Int] = Try(
xs.map(_.productElement(n).asInstanceOf[Int]).sum
)
And then:
然后:
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: ...
This is an improvement, since it forces callers to address the possibility of failure, but it's also kind of annoying, since it forces callers to address the possibility of failure.
这是一种改进,因为它迫使调用者解决失败的可能性,但它也有点烦人,因为它迫使调用者解决失败的可能性。
If you're willing to use Shapeless you can have the best of both worlds:
如果你愿意使用Shapless,你可以两全其美:
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
And then:
然后:
scala> sumNth(a, 1)
res7: Int = 5
But the bad ones don't even compile:
但糟糕的版本甚至都不会编译:
scala> sumNth(List((1, 2), (2, 3)), 3)
<console>:17: error: could not find implicit value for parameter atN: ...
This still isn't perfect, though, since it means the second argument has to be a literal number (since it needs to be known at compile time):
然而,这仍然不是完美的,因为它意味着第二个参数必须是文字数(因为它需要在编译时知道):
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)
^
In many cases that's not a problem, though.
不过,在许多情况下,这并不是问题。
To sum up: If you're willing to take responsibilty for reasonable code crashing your program, use productElement
. If you want a little more safety (at the cost of some inconvenience), use productElement
with Try
. If you want compile-time safety (but some limitations), use Shapeless.
总而言之:如果您愿意为合理的代码崩溃承担责任,那么可以使用ProductElement。如果您想要更多的安全性(以一些不便为代价),可以结合使用ProductElement和Try。如果您想要编译时安全(但有一些限制),请使用Shapless。
Antoher typesafe way to do it without using shapeless
is to provide a function to extract the element you need:
在不使用Shapless的情况下,类型安全的方法是提供一个函数来提取所需的元素:
def sumNth[T, E: Numeric](array: Array[T])(extract: T => E) =
array.map(extract).sum
Then you can define sum2nd
like this:
然后您可以这样定义SumSecond:
def sum2nd(array: Array[(Int, Int, Int)]): Int = sumNth(array)(_._2)
Or like this:
或者像这样:
val sum2nd: Array[(Int, Int, Int)] => Int = sumNth(_)(_._2)
You could do something like this, though it's not really type safe:
您可以这样做,尽管它并不是真正的类型安全:
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
Since Scala 3, elements of a tuple can be accessed by indexed with a function invocation-like syntax (e.g. t(1)
to get the second element of a tuple t
). The sumNth
method can thus be implemented like so:
从Scala3开始,可以通过使用类似于函数调用的语法进行索引来访问元组的元素(例如,t(1)以获取元组的第二个元素t)。因此,SumNth方法可以按如下方式实现:
def sumNth(arr: Array[(Int, Int, Int)], n: Int) = arr.map(_(n).asInstanceOf[Int]).sum
Scastie Demo
SCastie演示
更多回答
This is a reasonable solution, if you ignore the fact that the OP is asking for a specific signature / usage. :)
如果忽略OP要求特定签名/用法这一事实,这是一个合理的解决方案。:)
Thanks for adding this answer, which I have upvoted. I just want to note that although Scala 3 has added this, the comments above about tuples being the wrong choice here still apply (except perhaps in some highly unusual contexts). Tuples are not collections.
谢谢你的回答,我已经投了赞成票。我只想指出,尽管Scala 3已经添加了这一点,但上面关于元组是错误选择的评论仍然适用(除非在一些非常不寻常的上下文中)。元组不是集合。
我是一名优秀的程序员,十分优秀!