4

我正在尝试对可能包含缺失值的列表进行一些算术运算。到目前为止,我用 Option[Int] 表示我的列表:

val mylist=List( Option(4), Option(8), None )

通过这种表示,我可以轻松地在列表上应用一个函数(例如,乘以 2):

scala> mylist.map(_.map(_*2))
res2: List[Option[Int]] = List(Some(8), Some(16), None)

但是,这看起来比它需要的更复杂,所以我想知道我是否遗漏了一些东西。

另外,我不知道如何写总和之类的东西。我想应该可以使用(大)reduce 表达式......

所以,我想知道是否:

  • List[Option[Int]] 是这个用例的一个很好的表示
  • mylist.map(_.map(_*2))是最好的映射方式
  • 有没有一种简单的方法来求和?
4

5 回答 5

1
  • List[Option[Int]] 是这个用例的一个很好的表示

是否可以使用 flatMap 更早地将其展平?例如,如果您使用地图创建此列表,则可以使用 flatMap 而不是缺失值。我的建议是尽可能不表示缺失值。如果您需要代表他们,Option 是理想的选择。

  • mylist.map(_.map(_*2))是最好的映射方式
  • 有没有一种简单的方法来求和?

嵌套地图可能更可取。你也foldLeft可以。

foldLeft如果您需要做除 sum/product 之外的其他事情,这也很有帮助。

scala> val mylist=List( Option(4), Option(8), None )
mylist: List[Option[Int]] = List(Some(4), Some(8), None)

scala> mylist.foldLeft(0){
     |   case (acc, Some(i)) => acc + i 
     |   case (acc, _) => acc
     | }
res7: Int = 12

scala> (0 /: mylist) {
     |   case (acc, Some(i)) => acc + i 
     |   case (acc, _) => acc
     | }
res8: Int = 12

scala> (0 /: mylist) {
     |   case (acc, Some(i)) => acc - (i * 2)
     |   case (acc, _) => acc
     | }
res16: Int = -24
于 2012-11-25T03:35:35.287 回答
1

好吧,这不是我自己使用的模式,但如果值可能“缺失”,那么 anOption是合适的。但List可能不是。在一个List位置通常不是你应该依赖的东西,因为它不是随机访问。也许 aVector会更好,或者您需要考虑一种更好的方法来建模您的问题,即不要作为缺少值的列表。

Option您可以使用 for 表达式很好地处理:

for (o <- mylist; x <- o) yield x * 2

或展平列表:

mylist.flatten.map(_ * 2)

总结一下:

mylist.flatten.sum
于 2012-11-25T03:09:31.797 回答
0

最通用和最简洁的方法是使用Scalaz的 semigroup 类型类。这样,您不仅List[Option[Int]]可以将相同的功能应用于List[Int].

import scalaz._
import Scalaz._

object S {
  def double[A:Semigroup](l:List[A]) = l.map(x => x |+| x)
  def sum[A:Semigroup](l:List[A]) = l.reduce(_ |+| _)

  def main(a:Array[String]) {
    val l = List(Some(1), None, Some(2))
    val l2 = List(1,2)
    println(double(l))
    println(sum(l))
    println(double(l2))
    println(sum(l2))
  }

}

这打印

List(Some(2), None, Some(4))
Some(3)
List(2, 4)
3
于 2012-11-25T07:22:13.610 回答
0

所以,我想知道是否:

List[Option[Int]] 是这个用例的一个很好的表示

Option 绝对是表达缺失值的首选方式。您还可以考虑将其更改为List[(Int, Int)]第一个元素表示原始列表中的位置,第二个元素表示值的位置。

mylist.map(_.map(_*2))是最好的映射方式

在我看来,没有更短或更简洁的方式来表达这一点。(您有两个“级别”,这就是您需要两张地图的原因!)使用我建议的数据结构,这将变成mylist.map(t => (t._1, t._2*2)).

有没有一种简单的方法来求和?

没有比 om-nom-nom 建议的更简单的方法了。使用我的数据结构将是mylist.map(_._2).sum

于 2012-11-25T03:51:21.023 回答
0

我不确定您要达到的目标是什么,但这似乎不是正确的方法。如果您需要确定“列表”中是否存在某个值,那么 aSet可能会更好地满足您的需求:

scala> val s = Set(4,8)
s: scala.collection.immutable.Set[Int] = Set(4, 8)

scala> s(4)
res0: Boolean = true

scala> s(5)
res1: Boolean = false

scala> s map (_ * 2)
res2: scala.collection.immutable.Set[Int] = Set(8, 16)

scala> s reduceLeft((a,b) => a+b)
res3: Int = 12

甚至更简单:

scala> s sum
res4: Int = 12

如果您需要更复杂的东西,并且列表中元素的索引很重要,您可以使用Map键模拟索引的位置,并且可以将缺失值映射到某个默认值:

scala> val m = Map(1 -> 4, 2 -> 8) withDefault(n => 0)
m: scala.collection.immutable.Map[Int,Int] = Map(1 -> 4, 2 -> 8)

scala> m(1)
res5: Int = 4

scala> m(3)
res6: Int = 0

scala> m map {case (k,v) => (k,2*v)}
res7: scala.collection.immutable.Map[Int,Int] = Map(1 -> 8, 2 -> 16)

scala> m.foldLeft(0){case (sum,(_,v)) => sum+v}
res8: Int = 12

再次,不确定您的需求是什么,但感觉就像您走错了路……
Option是一种我在使用前会三思而后行的类型。问问自己是否真的有必要。可能有一些解决方案可以以更优雅的方式实现您想要做的事情。

于 2012-11-25T12:13:53.683 回答