【问题标题】:Arithmetic operations on list with missing values具有缺失值的列表的算术运算
【发布时间】:2012-11-25 02:55:55
【问题描述】:

我正在尝试对可能包含缺失值的列表进行一些算术运算。 到目前为止,我用 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)) 是最好的映射方式
  • 有没有简单的求和方法?

【问题讨论】:

  • 要对值求和,您可以使用mylist.flatten.sum
  • 有人对所有答案投了反对票
  • @drstevens 事件报告
  • @drstevens 和 om-nom-nom:我很好奇,为什么会有人这样做? (除非他们对除了他们自己的之外的所有答案都投了反对票?)
  • @drstevens 这很奇怪。我确实一直在这里投票,但我从来没有投票过任何东西

标签: list scala math missing-data


【解决方案1】:

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

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

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

或展平列表:

mylist.flatten.map(_ * 2)

总结一下:

mylist.flatten.sum

【讨论】:

  • 对于理解和展平不会保留无,看起来@scala_newbie 想保存它们。如果没有,还有另外一种方法:flatMap them:mylist.flatMap(_ * 2)
  • 实际上@om-nom-nom,flatMapping 不起作用(我也有同样的想法),因为函数的参数类型必须是Option[Int]
【解决方案2】:
  • 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

【讨论】:

    【解决方案3】:

    所以,我想知道:

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

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

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

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

    有没有简单的求和方法?

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

    【讨论】:

      【解决方案4】:

      最通用和最简洁的方法是使用Scalaz 的半群类型类。这样一来,您就不会局限于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
      

      【讨论】:

        【解决方案5】:

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

        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 是我在使用前会三思而后行的类型。问问自己是否真的有必要。可能有一些解决方案可以以更优雅的方式实现您想要做的事情。

        【讨论】:

        • 在我的用例中,我有一个对象序列。我需要对这些对象的属性进行统计,但这些对象有(不同的)缺失属性。我不确定如何在这里使用这些技术
        • 使用Map[YourObjects,List[Attributes]]怎么样?
        • 我不确定我是否理解您的建议。如果没有选项,我如何将我的属性存储在 list 中?如果您建议只保留 existent 属性的列表,我将如何区分它们(它们的索引不会固定)?
        • 我建议只保留现有属性的列表。假设您有一个属性 trait:MyAttribute,然后,对于每个需要的属性,您都可以将 trait 扩展为一个真实值。比如:SomeAttribute &lt;: MyAttribute & AnotherAttribute &lt;: MyAttribute 等等'...好吧,然后你可以像我建议的那样使用List[MyAttribute] 的地图(属性索引可以通过MyAttribute trait 中的抽象方法检索)
        • 或者,如果属性真的很简单,并且创建超级特征是一种“矫枉过正的解决方案”,您可以使用:Map[YourObject,Map[Int,Attribute]]
        猜你喜欢
        • 2017-11-19
        • 2013-12-28
        • 2019-03-04
        • 1970-01-01
        • 2019-09-20
        • 2016-05-21
        • 2020-07-16
        • 1970-01-01
        • 2019-06-02
        相关资源
        最近更新 更多