我已经明白,我可以使用List.sum
轻松地总结一个列表:
var mylist = List(1,2,3,4,5)
mylist.sum
// res387: Int = 15
但是,我有一个包含None
和Some(1)
等元素的列表。这些值是在运行左外连接后生成的。
现在,当我尝试运行List.sum
时,出现错误:
var mylist= List(Some(0), None, Some(0), Some(0), Some(1))
mylist.sum
<console>:27: error: could not find implicit value for parameter num: Numeric[Option[Int]]
mylist.sum
^
如何解决此问题?我可以以某种方式将None
和Some
值转换为整数,也许就在左外连接之后?
答案 0 :(得分:9)
您可以使用List.collect
方法进行模式匹配:
mylist.collect{ case Some(x) => x }.sum
// res9: Int = 1
这会忽略None
元素。
另一种选择是使用getOrElse
上的Option
来提取值,在这里您可以选择要替换None
的值:
mylist.map(_.getOrElse(0)).sum
// res10: Int = 1
答案 1 :(得分:7)
我发现处理Option[A]
集合的最简单方法是flatten
:
val myList = List(Some(0), None, Some(0), Some(0), Some(1))
myList.flatten.sum
对flatten
的调用将删除所有None
个值,并将剩余的Some[Int]
转换为普通的Int
- 最终为您留下Int
的集合}。
顺便说一下,拥抱不变性是Scala中的一等公民,并且更喜欢val
到var
。
答案 2 :(得分:2)
如果您想避免使用flatten
或map
创建额外的中间集合,则应考虑使用Iterator
,例如
mylist.iterator.flatten.sum
或
mylist.iterator.collect({ case Some(x) => x }).sum
或
mylist.iterator.map(_.getOrElse(0)).sum
我认为第一种和第二种方法更好一点,因为它们可以避免不必要的添加0.我可能会采用第一种方法,因为它很简单。
如果您想要有点花哨(或需要额外的通用性),您可以定义自己的Numeric[Option[Int]]
实例。类似这样的内容适用于Option[N]
类型N
类型Numeric
本身有Option[Int]
个实例,即Option[Double]
,Option[BigInt]
,Option[Option[Int]]
,{ {1}}等等。
implicit def optionNumeric[N](implicit num: Numeric[N]) = {
new Numeric[Option[N]] {
def compare(x: Option[N], y: Option[N]) = ??? //left as an exercise :-)
def fromInt(x: Int) = if (x != 0) Some(num.fromInt(x)) else None
def minus(x: Option[N], y: Option[N]) = x.map(vx => y.map(num.minus(vx, _)).getOrElse(vx)).orElse(negate(y))
def negate(x: Option[N]) = x.map(num.negate(_))
def plus(x: Option[N], y: Option[N]) = x.map(vx => y.map(num.plus(vx, _)).getOrElse(vx)).orElse(y)
def times(x: Option[N], y: Option[N]) = x.flatMap(vx => y.map(num.times(vx, _)))
def toDouble(x: Option[N]) = x.map(num.toDouble(_)).getOrElse(0d)
def toFloat(x: Option[N]) = x.map(num.toFloat(_)).getOrElse(0f)
def toInt(x: Option[N]) = x.map(num.toInt(_)).getOrElse(0)
def toLong(x: Option[N]) = x.map(num.toLong(_)).getOrElse(0L)
override val zero = None
override val one = Some(num.one)
}
}
示例:
List(Some(3), None, None, Some(5), Some(1), None).sum
//Some(9)
List[Option[Int]](Some(2), Some(4)).product
//Some(8)
List(Some(2), Some(4), None).product
//None
List(Some(Some(3)), Some(None), Some(Some(5)), None, Some(Some(1)), Some(None)).sum
//Some(Some(9))
List[Option[Option[Int]]](Some(Some(2)), Some(Some(4))).product
//Some(Some(8))
List[Option[Option[Int]]](Some(Some(2)), Some(Some(4)), None).product
//None
List[Option[Option[Int]]](Some(Some(2)), Some(Some(4)), Some(None)).product
//Some(None) !?!?!
请注意,可能存在多种表示“零”的方式,例如None
或Some(0)
Option[Int]
,None
,{{1}}优先{{1}}。另外,请注意这种方法包含了如何将半群(没有附加身份)变成幺半群的基本思想。
答案 3 :(得分:0)
您可以使用.fold或.reduce手动实现2个选项的总和。但我会去@Psidom approach
答案 4 :(得分:0)
列表上的折叠是一种更优化的解决方案。要小心在集合上链接函数调用,因为您可能会多次迭代List
之类的内容。
更优化的方法看起来像
val foo = List(Some(1), Some(2), None, Some(3))
foo.foldLeft(0)((acc, optNum) => acc + optNum.getOrElse(0))