如何对BigDecimal列表进行左折叠?("重载的方法+无法应用")

4

我想为一个 BigDecimal 列表编写一个简短的函数式求和函数,并尝试使用以下代码:

def sum(xs: List[BigDecimal]): BigDecimal = (0 /: xs) (_ + _)

但是我收到了以下错误信息:
<console>:7: error: overloaded method value + with alternatives:
  (x: Int)Int <and>
  (x: Char)Int <and>
  (x: Short)Int <and>
  (x: Byte)Int
 cannot be applied to (BigDecimal)
       def sum(xs: List[BigDecimal]): BigDecimal = (0 /: xs) (_ + _)
                                                                ^

如果我使用Int,那个函数就能正常工作了。我猜测这是因为BigDecimal对+运算符进行了重载。有什么好的解决方法来处理BigDecimal吗?


请注意,在像这样不需要初始值的情况下,您可以使用reducedef sum(xs: List[BigDecimal]) = xs.reduce(_ + _) - Travis Brown
3
希望你能够乐于为此而努力,并知道已经有一个内置的“sum”函数可用,例如List(BigDecimal(1.1), BigDecimal(2.2)).sum。请注意保持原文意思不变,同时让内容更通俗易懂。 - Luigi Plinge
4个回答

17

问题出在初始值上。解决方案在这里,而且非常简单:

 sum(xs: List[BigDecimal]): BigDecimal = (BigDecimal(0) /: xs) (_ + _)

2
顺便提一下,大多数Scala集合都有sum方法,因此在这种特定的+操作情况下,您可以简单地编写List(BigDecimal(1), BigDecimal(3)).sum以获得scala.math.BigDecimal = 4 - om-nom-nom

2

foldLeft需要一个初始化值。

def foldLeft[B](z: B)(f: (B, A) ⇒ B): B

这个初始值(命名为z)必须与要折叠的类型相同:

(BigDecimal(0) /: xs) { (sum: BigDecimal, x: BigDecimal) => sum+x }
// with syntax sugar
(BigDecimal(0) /: xs) { _+_ }

如果您添加一个Int作为初始化值,foldLeft将如下所示:
(0 /: xs) { (sum: Int, x: BigDecimal) => sum+x } // error: not possible to add a BigDecimal to Int

2
在这种情况下(累加器的类型与列表中的项相同),您可以通过将列表中的第一个和第二个项相加来开始折叠 - 也就是说,您不一定需要一个起始值。Scala的reduce提供了这种类型的折叠:
def sum(xs: List[BigDecimal]) = xs.reduce(_ + _)

如果你的操作不是可结合的,还有reduceLeftreduceRight版本。


4
这个方法在空列表上无法正常运行,因此并不是一个好的解决方案。 - Rex Kerr
@Rex,是的,我应该提到这一点。我同意foldLeft可能是更好的解决方案(如果你不想使用普通的sum)。但了解reduce仍然是很好的。 - Travis Brown
此外,@Jonas(和@Rex),就记录而言,我不认为这应该是被接受的答案(这就是为什么我最初将其作为评论的原因)。 - Travis Brown

0

正如其他人所说,你之所以出现错误是因为初始值的问题,所以正确的方法是将其包装在BigDecimal中。此外,如果您有多个这样的函数并且不想在每个地方都写BigDecimal(value),您可以创建隐式转换函数,如下所示:

implicit def intToBigDecimal(value: Int) = BigDecimal(value)

下一次Scala将会自动将所有的Int(包括常量)转换为BigDecimal。实际上,大多数编程语言都使用从整数到小数甚至从小数到分数的静默转换(例如Lisp),因此这似乎是非常合理的举措。


2
为什么要在各个地方随意使用隐式转换。如果你的结果是一个BigDecimal,那么将你的起始值声明为BigDecimal是有意义的。 - AndreasScheinert
@AndreasScheinert:并不是到处都有问题,而是在数字转换方面。Java和Scala都存在数字类型之间的转换泄漏。如果你有Int,并想要与双精度浮点数一起在表达式中使用 - 没有问题,Java会将其转换为Double。那么在转换Int => Double(甚至Int => Long)和Int => BigDecimal之间有什么区别呢?这只是语言的逻辑扩展。请参阅SICP的此章节以获取更多有关此概念的信息。 - ffriend

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接