【问题标题】:Fold on Option vs Fold on Collection期权折叠 vs 收藏折叠
【发布时间】:2016-11-10 18:41:41
【问题描述】:

在 Scala 中,表达这一点:

val opt:Option[String] = Some("a")
if(opt.isEmpty) "" else opt.get

您可以在Options 上使用此fold 模式:

opt.fold("")(_)

但是 fold 在集合上的行为不同。我怎样才能在不写的情况下为集合实现同样的效果:

case class Container(description: String, collection: Seq)
val col = Seq()
if(col.nonEmpty) {
    Some(Container("some description", col))
} else {
    None
}

换句话说,我如何优雅地只在Some()集合不为空的情况下返回它?

【问题讨论】:

  • 你试过headOption吗?或headOption.map(whatevergoeshere)
  • 另外,if(opt.isEmpty) "" else opt.get 会更符合 opt.getOrElse("") 的习惯
  • 为什么if/else 不“优雅”?对我来说这是完美的发现。如果您出于某种原因不想要if/else,这是一种方法:Some(col).filter(_.nonEmpty).map(c => Container("some description", c))
  • 这种行为与Option 上的fold 有何相同之处?
  • 因为Some(col).filter(_.nonEmpty) 基本上是if (col.nonEmpty) Some(col) else None。然后,你 map 在你的 Container 构造函数上,它只转换 Some 案例,这意味着它与 if (col.nonEmpty) { val c = col; Some(Container("some description", c)) } else None 相同,正如你所看到的,它与你的“不优雅”sn 相同-p.

标签: scala collections scala-collections


【解决方案1】:

请注意,OptionSeq 的示例不等效:在前一种情况下,结果是 inside Option 的值或空字符串,而在后一种情况下,您最终会返回集合本身,而不是其内容。

您也可以在集合上使用 fold 来执行与 Option 相同的操作,具体取决于您希望如何处理集合中的多个值。例如:

seqOfStrings.foldLeft("") { _ + _ }

会将集合中的所有字符串连接成一个长字符串(使用seqOfStrings.mkString 可以获得相同的结果)。请注意,这使用foldLeft 而不是fold,因为前者是(应该是)可并行化的,并且不能保证按顺序处理结果。这是fold 的示例:

 seqOfInts.fold(0) { _ + _ }

这当然和seqOfInts.sum一样

fold(左/右)的想法是将集合的所有元素组合(“折叠”)为一个值。 Option 是一个特例,因为它只能包含一个元素,所以转换器函数不需要带两个参数,并且看起来很像 getOrElse 的(相反) - 因此你会感到困惑。

您尝试对集合执行的操作并不是折叠的真正用例。您可以使用headOption 作为其他答案的建议,或者更好的是模式匹配:

  col match {
     case Seq() => None
     case x => Some(Container("some description", x))
  }

你可以把它包装到伴生对象中,看起来更漂亮:

  object Container {
     def apply[T](seq: Seq[T]) = seq match {
        case Seq() => None
        case s => Container("some description", x))
     }
  }

现在,您可以在其他地方使用Container(seq)

【讨论】:

  • 这很好,我认为容器伴随对象是解决问题的最优雅的方法。谢谢!除此之外,我认为有 2 个函数具有相同的名称但基于上下文的不同行为,这有点愚蠢......
  • @mirosval 我不确定您所说的“不同行为”是什么意思。行为实际上完全相同:如果容器为空,则返回初始值,否则将转换应用于内容。
【解决方案2】:

您对fold 的期望是错误的,这与Options 无关,而是与集合一起工作,它与Option 一起工作,因为它们也是集合。

使用fold,您将提供带有第一个参数的起始值并提供将应用于集合的函数。

关于您的问题: 您共享的代码看起来不错,但如果您真的想要映射某个选项,您可以使用 headOption 并将其映射到您想要的值,但这不会让您的代码更优雅或更清晰

【讨论】:

    【解决方案3】:

    这有点短,虽然也不是很优雅:

    Option(col.nonEmpty).collect { case true => Container("some description", col) }
    

    或者,但 IMO 更丑陋(我刚刚看到 Hüseyin 也提出了建议):

    col.headOption.map(Container("some description", col))
    

    【讨论】:

      猜你喜欢
      • 2014-05-22
      • 2016-10-26
      • 2016-04-29
      • 1970-01-01
      • 2013-01-01
      • 2017-03-10
      • 2011-07-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多