首页 > 解决方案 > 迭代器:将最后一个元素与不同的值配对

问题描述

在 Scala 中,我有一个 List[String] ,如下所示:

val xs = List("apple", "orange", ..., "pear", "dragonfruit")

我想生成以下结果列表:

List(("apple", ","), ("orange", ","), ..., ("pear", ","), ("dragonfruit", ";"))

请注意,最后一对有一个分号而不是逗号。我可以在不知道列表长度的情况下执行此操作吗?更一般地说,是否有适用于 Iterable 的功能版本?

def pairUp[A,B](xs: Iterable[A], nonlastB: B, lastB: B): Iterable[(A,B)]

除了最后一个 xs 中的每个 x 都应该与 配对nonlastB,最后一个与lastB.

标签: scala

解决方案


用于foldRight此。

在您的示例中,您有

"apple" :: "orange" :: ... :: "pear" :: "dragonfruit" :: Nil

而你想要

("apple", ",") :: ("orange", ",") :: ... :: ("pear", ",") :: ("dragonfruit", ";") :: Nil

foldRight 的直觉是它会替换构造函数。

l.foldRight接受两个参数。第一个替换了构造函数Nil,第二个替换了构造函数::,所以你有l.foldRight(nilReplacement)(consreplacement)现在让我们看看你想要什么替换 forNil和 for ::

"apple"        :: "orange"        :: ... :: "pear"        :: "dragonfruit" :: Nil
("apple", ",") :: ("orange", ",") :: ... :: ("pear", ",") :: ("dragonfruit", ";") :: Nil
  • Nil停留Nil
  • x :: Nil变成(x, ";") :: Nil
  • x :: other变成(x, ",") :: other

所以

val nilReplacement = Nil
val consReplacement = {
  case x :: Nil   => (x, ";") :: Nil
  case x :: other => (x, ",") :: other
}

xs.foldRight(nilReplacement)(consReplacement)

不幸的是,您会发现val consreplacement由于没有预期的类型而无法正确进行类型检查,并且它Nil很快就会推断出错误的类型。但是给它足够的类型提示可以解决这个问题:

xs.foldRight[(String, String)](Nil) {
  case x :: Nil   => (x, ";") :: Nil
  case x :: other => (x, ",") :: other
}

一种完全不同的方法通过迭代直接进行:

import scala.collection.mutable.Builder

def pairUp[A,B](as: Iterable[A], nonlastB: B, lastB: B): Iterable[(A,B)] = {
  val it = as.iterator
  val builder = as.iterableFactory.newBuilder[(A, B)]
  def loop(prev: A): Unit = {
    if (it.hasNext) {
      builder.addOne(prev -> nonlastB)
      loop(it.next())
    } else builder.addOne(prev -> lastB)
  }
  if(it.hasNext) loop(it.next())
  builder.result()
}

您可以使用Factorystdlib 中有些复杂的基础结构来抽象输入类型:

def pairUpF[A,B, F[x] <: IterableOps[x, F, F[x]]](as: F[A], nonlastB: B, lastB: B): F[(A,B)] = {
  val it = as.iterator
  val builder = as.iterableFactory.newBuilder[(A, B)]
  def loop(prev: A): Unit = {
    if (it.hasNext) {
      builder.addOne(prev -> nonlastB)
      loop(it.next())
    } else builder.addOne(prev -> lastB)
  }
  if(it.hasNext) loop(it.next())
  builder.result()
}

推荐阅读