【问题标题】:How can I map across multiple elements (e.g. lookahead)?如何映射多个元素(例如前瞻)?
【发布时间】:2019-06-21 15:19:44
【问题描述】:

我有一个 List[Int],可能是这样的:

List(1,2,3,3,4,5,6,6,7,8,9)

偶尔会有重复(总是 2 个,而不是更多)。当有一个重复时,我想将元素与一个函数合并在一起。所以在这个简单的例子中,如果我的功能是将两个数字相加,我会得到:

List(1,2,6,4,5,12,7,8,9)

有什么简洁的方法可以做到这一点? List.map() 一次只查看/转换 1 个元素。

【问题讨论】:

  • 重复会一直持续吗?因为它们只会在相邻的位置?

标签: scala


【解决方案1】:

您可以将.foldLeft 与新列表一起用作累加器(假设列表彼此相邻)

def mergeDuplicates(list: List[Int]): List[Int] = {
  list.foldLeft(List.empty[Int]) {
    case (l, elem) if l.lastOption.contains(elem) =>
      l.dropRight(1) :+ (2 * elem)
    case (l, elem) =>
      l :+ elem
  }
}

println(mergeDuplicates(List(1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9))) 

// output List(1, 2, 6, 4, 5, 12, 7, 8, 9)

使用extension method,

implicit class ListOps(list: List[Int]) {
  def mergeDuplicates: List[Int] = {
    list.foldLeft(List.empty[Int]) {
      case (l, elem) if l.lastOption.contains(elem) =>
        l.dropRight(1) :+ (2 * elem)
      case (l, elem) =>
        l :+ elem
    }
  }
}

val mergedList = List(1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9).mergeDuplicates
println(mergedList)

【讨论】:

  • 我认为foldRight 和前置:: 而不是foldLeft 并附加:+ 更有意义。尤其是在构建List。
  • 特别是因为 lastOption 在列表中的 O(n) 相当高,最好在前面加上 headOption 并在最后反转
【解决方案2】:

从Scala 2.13 开始,您可以使用List#unfold 与List#span 结合使用:

// val items = List(1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9)
List.unfold(items) {
  case Nil  => None
  case rest => Some(rest.span(_ == rest.head))
}
.map(_.sum)
// List(1, 2, 6, 4, 5, 12, 7, 8, 9)

甚至,再加上Scala 2.13的Option#unless builder:

List
  .unfold(items)(rest => Option.unless(rest.isEmpty)(rest.span(_ == rest.head)))
  .map(_.sum)

  • Unfold 使用内部状态,在我们的例子中使用列表 List(1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 9) 初始化,用于对连续重复项求和。
  • 在每次迭代期间,我们 span 内部状态以查找标题连续项:rest.span(_ == rest.head)(例如在 List(3, 3, 4, 5, ...) 内部状态的第三次迭代期间提供 (List(3, 3), List(5, 6, ...)))。
  • 因为unfold 期望在每次迭代中都有一个元组的Option,其第一部分是要添加到正在构建的列表中的新元素(在第一次迭代期间,例如List(1)),其第二部分是新元素内部状态 (List(2, 3, 3, ...)) 的值,该跨度完全符合该要求。
  • 我们不断迭代相同的步骤,直到内部列表为空,在这种情况下,返回 None 表示我们已完成并且必须停止迭代的 unfold。
  • 最后,我们将分组部分 (List(List(1), ..., List(3, 3), ...)) 替换为简单的 .map(_.sum)。当然,这也可以在展开过程中以一些额外的样板为代价来实现。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-01-19
    • 2018-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多