如何使用惯用的Scala替换(填充)来自另一个列表的选项列表中的None条目?

Posted

技术标签:

【中文标题】如何使用惯用的Scala替换(填充)来自另一个列表的选项列表中的None条目?【英文标题】:How to replace(fill) None entries on List of Options from another List using idiomatic Scala? 【发布时间】:2012-06-23 17:49:12 【问题描述】:

我有一个List[Option[MyClass]]None 在随机位置,我需要从List[MyClass] 再次“填充”该列表,以保持顺序。

以下是示例列表和预期结果:

val listA = List(Some(3),None,Some(5),None,None)
val listB = List(7,8,9)
val expectedList = List(Some(3), Some(7), Some(5), Some(8), Some(9))

那么,如何使用惯用的 Scala 来处理该列表?

【问题讨论】:

【参考方案1】:
def fillL[T](a:List[Option[T]], b:List[T]) = 
    val iterB = b.iterator
    a.map(_.orElse(Some(iterB.next)))

【讨论】:

【参考方案2】:

迭代器解决方案可以说是惯用的 Scala,而且绝对简洁且易于理解,但它不是功能性 — 任何时候你在迭代器上调用 next副作用。

更实用的方法是使用折叠:

def fillGaps[A](gappy: List[Option[A]], filler: List[A]) =
  gappy.foldLeft((List.empty[Option[A]], filler)) 
    case ((current, fs), Some(item)) => (current :+ Some(item), fs)
    case ((current, f :: fs), None) => (current :+ Some(f), fs)
    case ((current, Nil), None) => (current :+ None, Nil)
  ._1

在这里,我们通过 gappy 列表移动,同时维护另外两个列表:一个用于我们已处理的项目,另一个用于剩余的填充元素。

这种解决方案不一定比其他解决方案更好 - Scala 旨在允许您以这种方式混合函数式和命令式结构 - 但它确实具有潜在的优势。

【讨论】:

“任何时候你在迭代器上调用 next 时,你都牢牢地处于副作用的境地。”没错,但在这种情况下,它们被巧妙地封装在方法中,保持引用透明。 @Paul:是的,我认为另一种解决方案很棒,这是我选择在我自己的代码中解决这个问题的方法。但它确实涉及副作用,并且在某些类似的情况下可能并不理想。【参考方案3】:

我只是以简单的方式编写它,匹配列表的头部并适当地处理每个案例:

def fill[A](l1: List[Option[A]], l2: List[A]) = (l1, l2) match 
  case (Nil, _) => Nil
  case (_, Nil) => l1
  case (Some(x) :: xs, _) => Some(x) :: fill(xs, l2)
  case (None :: xs, y :: ys) => Some(y) :: fill(xs, ys)

大概一旦你用完了可以填满的东西,你就把剩下的 Nones 留在里面。

【讨论】:

以上是关于如何使用惯用的Scala替换(填充)来自另一个列表的选项列表中的None条目?的主要内容,如果未能解决你的问题,请参考以下文章

scala upickle / ujson中JSON null的惯用处理

java adapter(适配器)惯用方法

将 Lambda 应用于返回另一个列表的列表的 Java 8 惯用方法?

Scala 2.10 - 八进制转义已被弃用 - 现在如何惯用八进制?

如何在Scala中按另一个列表拆分列表

Scala Spark用NULL替换空String