Best way to score and sum in Scala?
Is there a better wa开发者_Go百科y of doing this:
val totalScore = set.foldLeft(0)( _ + score(_) )
or this:
val totalScore = set.toSeq.map(score(_)).sum
I think it's quite a common operation so was expecting something sleeker like:
val totalScore = set.sum( score(_) )
Well, there are alternative ways to write it:
val totalScore = set.toSeq.map(score(_)).sum
val totalScore = set.toSeq.map(score).sum
val totalScore = set.toSeq map score sum
The last one may require a semi-colon at the end if the next line doesn't start with a keyword. One can also use .view
instead of .toSeq
, which would avoid allocating a temporary collection. However, I'm not sure the .view
's present behavior (of showing repeated elements) is the correct one.
Seq.sum
does not take a function which could be used to score the sum. You could define an implicit conversion which "pimps" Traversable
:
implicit def traversableWithSum[A](t: Traversable[A])(implicit m: Numeric[A]) = new {
def sumWith(f: A => A) = t.foldLeft(m.zero)((a, b) => m.plus(a, f(b)))
}
def score(i: Int) = i + 1
val s = Set(1, 2, 3)
val totalScore = s.sumWith(score _)
println(totalScore)
=> 9
Please note that the Numeric
trait only exists in Scala 2.8.
Simpler:
scala> val is1 = Set(1, 4, 9, 16)
is1: scala.collection.immutable.Set[Int] = Set(1, 4, 9, 16)
scala> is1.reduceLeft(_ + _)
res0: Int = 30
With your score method:
scoreSet.reduceLeft(_ + score(_))
Warning, though, this fails is the collection being reduced is empty while fold does not:
scala> val is0 = Set[Int]()
is0: scala.collection.immutable.Set[Int] = Set()
scala> is0.foldLeft(0)(_ + _)
res1: Int = 0
Alternately, the Seq#sum
overload that takes an implicit conversion to Numeric
could be used if the type in the collection to be scored / summed does not itself have an addition operator. However, because it's an implicit conversion parameter, it won't be applied unless required to make the reduce closure type-check.
精彩评论