如何习惯性地迭代flatMap一个集合对自己的成员?

时间:2013-02-19 15:05:48

标签: scala scala-collections

一个带有flatMap / map的简单类,除了延迟存储值之外什么都不做:

[注意1:此类可以替换为带有flatMap / map的任何类。选项只是一个具体的例子,这个问题是关于一般情况的]

[注2:scalaz是一个有趣的库,但这个问题不是关于它的。如果没有我在下面发布的标准scala库解决方案,那是可以接受的。]

class C[A](value : => A) {
   def flatMap[B](f: A => C[B]) : C[B] = { f(value) }
   def map[B](f: A => B) : C[B] = { new C(f(value)) }
   override def toString = s"C($value)"
}
object C {
   def apply[A](value : => A) = new C[A](value)
}

将flatMap迭代地应用于其成员的函数:

def invert[A](xs: Traversable[C[A]], acc: List[A] = Nil) : C[List[A]] =
  if(xs.nonEmpty) {
    xs.head flatMap { a => invert(xs.tail, a :: acc) }
  } else {
    C(acc.reverse)
  }

行动中的功能:

scala> val l = List(C(1),C(2),C(3))
l: List[C[Int]] = List(C(1), C(2), C(3))

scala> invert(l)
res4: C[List[Int]] = C(List(1, 2, 3))     

有没有办法重写“反转”?另外,是否有一个功能性的“动词”来捕捉我在这里做的事情?

2 个答案:

答案 0 :(得分:1)

您的解决方案的问题在于它会为大型列表提供堆栈溢出,因为它完全(不仅仅是尾部)递归。我会弃牌:

def invert[A](xs: Traversable[C[A]]) =
  (C(List[A]()) /: xs){ (c,x) => c.flatMap(l => x.map(_ :: l)) }.map(_.reverse)

答案 1 :(得分:0)

您可以通过模式匹配使invert更清晰一点,但它基本上是相同的代码:

xs match {
  case x0 :: xMore => x0.flatMap(a => invert(xMore, a :: acc))
  case Nil => C(acc.reverse)
}