代码之家  ›  专栏  ›  技术社区  ›  Lunivore

如何将此foldLeft:double表达式转换为使用选项[double]?

  •  9
  • Lunivore  · 技术社区  · 14 年前

    有人能帮这个斯卡拉新手吗?之前,我们将实体列表中的数量与这些数量相加,得出:

    sum = entities.foldLeft(0.0)(_ + _.quantity)
    

    现在数量是 Option[Double] ,和也是。如何使用惯用scala转换它?

    如果任何实体的数量为 None 那么总数也应该是 没有 . 否则,总和应为 Some(total) .

    编辑:把这个东西放进一个单元测试,这样我就可以尝试你的所有答案。请注意,如果 任何 数量是没有的,因为缺少数量意味着我们还没有完成,所以总数应该反映这一点。即使你没有得到正确的答案,如果你帮助引导我或其他人,或帮助我学习新的东西,我会投赞成票。

    编辑:@sepp2k赢得一个有效的解决方案加解释。感谢大家的学习!

    10 回复  |  直到 10 年前
        1
  •  9
  •   sepp2k    14 年前

    你可以使用 Option flatMap map 结合两种方法 选择权 所以结果是 Some(f(x,y)) 如果两者 选择权 S是 Some(x) Some(y) None 否则。

    entities.foldLeft(Some(0.0):Option[Double]) {
        (acco, x) => acco.flatMap(acc => x.quantity.map(_ + acc))
    }
    

    根据您的评论进行编辑:

    下面是一个示例用法:

    scala> case class Foo(quantity:Option[Double]) {}
    defined class Foo
    scala> val entities: List[Foo] = List(Foo(Some(2.0)), Foo(Some(1.0)), Foo(None))
    scala> entities.foldLeft(Some(0.0):Option[Double]) {
        (acco, x) => acco.flatMap(acc => x.quantity.map(_ + acc))
    }
    res0: Option[Double] = None
    
    scala> val entities: List[Foo] = List(Foo(Some(2.0)), Foo(Some(1.0)))                                  
    scala> entities.foldLeft(Some(0.0):Option[Double]) {
        (acco, x) => acco.flatMap(acc => x.quantity.map(_ + acc))
    }
    res1: Option[Double] = Some(3.0)
    

    是的,它会回来的 没有 如果任何实体 没有 .

    关于 地图 平版地图 :

    地图 接受一个函数 f 类型的 A => B 回报 Some(f(x)) 对于 一些(X) 没有 对于 没有 .

    xo.flatMap(f) 在哪里 f 是类型的函数 A => Option[B] xo 是一个 Option[A] 回报 一些(Y) 敌我识别 XO 一些(X) f(x) 一些(Y) . 在所有其他情况下(即 XO 没有 f(x) 没有 它回来了 没有 .

    所以表达式 acco.flatMap(acc => x.quantity.map(_ + acc)) 收益率 y + acc 敌我识别 x.quantity 一些(Y) acco Some(acc) . 如果一个或两个 X量 阿科 没有 ,结果将为“无”。因为这是在一个褶皱内,这意味着在下一次迭代中 阿科 也将 没有 因此最终结果是 没有 .

        2
  •  4
  •   olle kullberg    14 年前

    我喜欢使用 for 使用时 Option :

    // ========= Setup ===============
    case class Entity(x: Double){
      // Dummy
      def quantity = if (x < 2) None
        else Some(x)
    }
    
    val entities = List(Entity(1), Entity(5), Entity(7))
    
    // ========= Calculate ===============
    val quantities = for{
       entity <- entities
       q <- entity.quantity
    } yield q
    
    val qSum = quantities.sum
    

    对于Java用户来说,这应该是很容易的。

    (抱歉实施了 Entity 我很难想出一个 quantity() 在某些点上实际上没有返回的实现。)

    编辑:添加的解释

    你想要的是计算总数,对吗?如果 数量() 收益率 None 对于列表中的所有实体,总和将为0。为什么?因为 quantities 集合不包含任何元素。

    使用时 选择权 具有 对于 你可以全部删除 没有 以非常好的方式从结果列表中提取元素。这是一条线:

     q <- entity.quantity 
    

    …这实际上消除了所有 没有 结果列表中的结果并提取 Double Some(x) . 所以:

    yield q
    

    …只会回来 双重的 类型。这使您有机会在结果集合上使用sum()函数,因为集合保留 双重的 而不是 Option[Double] . 这个 sum 操作非常易读!

        3
  •  3
  •   Apocalisp    14 年前

    “惯用语”非常贴切,因为它被称为 idiomatic function application 也就是说,“提升”一个函数成为“习语”(更现代的说法是:“应用函数”)。

    Scalaz ,可以按如下方式进行:

    import scalaz._
    import Scalaz._
    
    val add: (Int, Int) => Int = (x, y) => x + y
    val addOptions: (Option[Int], Option[Int]) => Option[Int] = add.lift
    

    或者像这样:

    List(1,2,3).map(some(_)).foldLeft(some(0))(add.lift)
    
        4
  •  2
  •   Eric Bowman - abstracto -    14 年前

    好吧,我想这就是你想要的。(先前的回答误解了您的要求)。

    entities.find(_.quantity == None) match {
      case Some(_) => None
      case None => Some(entities.map(_.quantity).flatten.reduceLeft(_ + _))
    }
    

    我认为另一个答案更“惯用”,但在我看来,这更容易理解。

        5
  •  2
  •   Moritz    14 年前

    编辑: 由于实体也是一个可选的值代码

    而@seppak的答案是正确的,如果您有 Option[Entity] 用一个 Double quantity 字段您需要的内容如下:

    entities.foldLeft(Option(0d)) {
      (sum, oe) => for {
        s <- sum
        e <- oe
        q <- e.quantity
      } yield s + q
    }
    

    这个 for -闭包内的理解等同于 flatMap / map 就像@sepp2k的答案一样,但在我的经验中,初学者更容易阅读。

        6
  •  2
  •   Rex Kerr    14 年前

    许多现有的解决方案都可以工作(而被接受的解决方案是规范的,并且是我通常使用的解决方案),但是这里有一个解决方案如果碰到 None 是常见的;它在第一次命中时使评估短路 没有 . 注意这是尾部递归。

    // Replace Option[Double] by your entity type, and it.next with it.next.quantity
    def total(it: Iterator[Option[Double]], zero: Double = 0.0): Option[Double] = {
      if (it.hasNext) {
        it.next match {
          case Some(x) => total(it,zero+x)
          case None => None
        }
      }
      else Some(zero)
    }
    // To use: total(entities.iterator)
    
        7
  •  2
  •   Marco Faustinelli    10 年前

    别忘了那个大哥哥 fold right 一旦遇到“无”,就可以退出递归。一切都非常优雅:

    def sumOptsFoldRight = (entities:List[Option[Double]]) =>    
        entities.foldRight(Some(0.0):Option[Double])((accOpt,xOpt) => xOpt match {
            case None => None
            case Some(xVal) => accOpt.map(xVal + _)
        })
    
        8
  •  1
  •   Synesso    14 年前

    我会明确检查是否没有使用

    entities.forall(_.quantity.isDefined)
    

    例如:

    scala> case class Entity(quantity: Option[Double])
    defined class Entity
    
    scala> val entities = List(Entity(Some(10.0)), Entity(None), Entity(Some(15.0)))
    entities: List[Entity] = List(Entity(Some(10.0)), Entity(None), Entity(Some(15.0)))
    
    scala> if (entities.forall(_.quantity.isDefined)) {
         |   Some(entities.flatMap(_.quantity).reduceLeft(_+_))
         | } else None
    res6: Option[Double] = None
    
        9
  •  1
  •   Landei    14 年前
    val sum = entities.foldLeft(Some(0.0):Option[Double]){
      (s,e) => if (s.isEmpty || e.quantity.isEmpty) None else Some(s.sum + e.quantity.sum)}
    

    val sum = if(entities.exists(_.quantity.isEmpty)) None
              else Some(entities.flatMap(_.quantity).sum)
    
        10
  •  1
  •   Community basarat    7 年前

    这个答案和 sepp2k / Moritz 但为了让事情更清楚,它分为两个功能。

    def addOptionDouble(optionalA: Option[Double], optionalB: Option[Double]): Option[Double] =
      for {
        a <- optionalA
        b <- optionalB
      } yield a + b
    
    def sumQuantitiesOfEntities(entities: Traversable[Entity]): Option[Double] = 
      entities.foldLeft(Option(0.0)) {
        (acc, entity) => addOptionDouble(acc, entity.quantity)
      }