【问题标题】:Scala: map with two or more OptionsScala:具有两个或更多选项的映射
【发布时间】:2011-04-29 13:14:39
【问题描述】:

基本上,我正在寻找最类似于 scala 的方式来执行以下操作:

def sum(value1: Option[Int], value2: Option[Int]): Option[Int] = 
  if(value1.isDefined && value2.isDefined) Some(value1.get + value2.get)
  else if(value1.isDefined && value2.isEmpty) value1
  else if(value1.isEmpty && value2.isDefined) value2
  else None

这给出了正确的输出:

sum(Some(5), Some(3))  // result = Some(8)
sum(Some(5), None)     // result = Some(5)
sum(None, Some(3))     // result = Some(3)
sum(None, None)        // result = None

但要总结两个以上的选项,我不得不使用太多 ifs 或使用某种循环。

EDIT-1:

在写这个问题时,我想出了一个答案:

def sum2(value1: Option[Int], value2: Option[Int]): Option[Int] = 
  value1.toList ::: value2.toList reduceLeftOption { _ + _ }

对于我没有经验的人来说,这个看起来非常地道。这甚至可以使用两个以上的值。然而,是否可以在不转换为列表的情况下做同样的事情?

EDIT-2:

我最终得到了这个解决方案(感谢 ziggystar):

def sum(values: Option[Int]*): Option[Int] = 
  values.flatten reduceLeftOption { _ + _ }

EDIT-3:

感谢Landei的另一种选择:

def sum(values: Option[Int]*): Option[Int] = 
  values collect { case Some(n) => n } reduceLeftOption { _ + _ }

【问题讨论】:

    标签: scala scala-option


    【解决方案1】:

    怎么样:

    scala> def sum(values: Option[Int]*): Option[Int] = values.flatten match {
         | case Nil => None                                                   
         | case l => Some(l.sum)                                              
         | }
    sum: (values: Option[Int]*)Option[Int]
    
    scala> sum(Some(1), None)
    res0: Option[Int] = Some(1)
    
    scala> sum(Some(1), Some(4))
    res1: Option[Int] = Some(5)
    
    scala> sum(Some(1), Some(4), Some(-5))
    res3: Option[Int] = Some(0)
    
    scala> sum(None, None)                
    res4: Option[Int] = None
    

    编辑

    如果所有参数都为“无”,则返回 0 可能是明智的。在这种情况下,函数将减少到 values.flatten.sum

    【讨论】:

    • 感谢flatten 我想这就是我所缺少的。
    【解决方案2】:
    scala> def sum(a: Option[Int], b: Option[Int]) = (a,b) match {
         |   case (Some(x), Some(y)) => Some(x + y)
         |   case (Some(x), None) => Some(x)
         |   case (None, Some(y)) => Some(y)
         |   case _ => None
         | }
    sum: (a: Option[Int],b: Option[Int])Option[Int]
    
    scala> sum(Some(5), Some(3))
    res0: Option[Int] = Some(8)
    
    scala> sum(Some(5), None)
    res1: Option[Int] = Some(5)
    
    scala> sum(None, Some(3))
    res2: Option[Int] = Some(3)
    
    scala> sum(None, None)
    res3: Option[Int] = None
    

    【讨论】:

    • 谢谢。从某种意义上说,使用match 而不是if 更为惯用。否则,它与我的第一个示例的代码相同,但限制相同 - 您必须添加更多 cases 才能对超过 2 个值求和。
    【解决方案3】:

    另一种解决方案是:

    def sum(values: Option[Int]*): Int = values.collect{case Some(n) => n}.sum
    

    虽然在当前情况下flatten 显然更方便,但collect 版本更灵活,因为它允许执行映射并具有额外的过滤条件或复杂模式。例如。想象一下,你想得到所有偶数平方和的值:

    values.collect{case Some(n) if n mod 2 == 0 => n*n}.sum
    

    【讨论】:

    • 编译器抱怨返回类型:找到Int,需要Option[Int]。我明白你关于collect 方法而不是flatten 的观点,但我仍然需要做reduceLeftOption 才能获得所需的结果。
    【解决方案4】:

    您可以使用 Semigroup 的实例来使其非常简洁,因为 Option 的实例完全符合您的要求。您可以使用scalazcats。这是一个使用cats的例子:

    import cats.std.option._
    import cats.syntax.semigroup._
    import cats.std.int._
    
    Option(1) |+| Option(2) // Some(3)
    Option(1) |+| None      // Some(1)
    None      |+| Option(2) // Some(2)
    

    所以你的sum 变成:

    def sum(v1: Option[Int], v2: Option[Int]): Option[Int] = v1 |+| v2
    

    【讨论】:

      【解决方案5】:

      michael.kebe 的简化解,稍微了解一些基本数学规则:

      def sum(a: Option[Int], b: Option[Int]) = (a,b) match {
        case (None,None) => None
        case _ => Some(a.getOrElse(0)+b.getOrElse(0))
      }
      
      scala> sum(Some(5), Some(3))  // result = Some(8)
      res6: Option[Int] = Some(8)
      
      scala> sum(Some(5), None)     // result = Some(5)
      res7: Option[Int] = Some(5)
      
      scala> sum(None, Some(3))     // result = Some(3)
      res8: Option[Int] = Some(3)
      
      scala> sum(None, None)        // result = None
      res9: Option[Int] = None
      

      【讨论】:

        猜你喜欢
        • 2018-12-19
        • 1970-01-01
        • 2021-06-18
        • 2021-09-07
        • 2021-12-22
        • 1970-01-01
        • 1970-01-01
        • 2015-09-19
        • 1970-01-01
        相关资源
        最近更新 更多