【问题标题】:Operating on Scala collections in generic way以通用方式操作 Scala 集合
【发布时间】:2011-08-09 16:38:30
【问题描述】:

我编写了查找longest common subsequence (LCS) 的函数。例如,对于两个字符序列 BANANA 和 ATANA,它返回 AANA。实现是递归算法的幼稚低效适应,但与此问题的目的无关。

def LCS[T](a: Seq[T], b: Seq[T]): Seq[T] = {
    if (a.isEmpty || b.isEmpty)
      Seq.empty
    else if (a.head == b.head)
      a.head +: LCS(a.tail, b.tail)
    else {
      val case1 = LCS(a.tail, b)
      val case2 = LCS(a, b.tail)
      if (case1.length > case2.length) case1 else case2
    }
}

我想以最通用的方式重构这个函数。当前实现适用于任何类型的输入序列,但总是返回 List[T] 类型的集合。我想实现以下行为:

LCS(List('B','A','N','A','N','A'), List('A','T','A','N','A' )) -> 列表('A','A','N','A') LCS(向量('B','A','N','A','N','A'),向量('A','T','A','N','A' )) -> 向量('A','A','N','A') ...对于所有其他 Seq 以此类推...

如果 LCS 也能处理 Strings 和 Arrays 那就太好了:

LCS(“香蕉”,“ATANA”)->“AANA” LCS(数组('B','A','N','A','N','A'),数组('A','T','A','N','A' )) -> 数组('A','A','N','A')

我相信在 Scala 2.8 通用集合库的帮助下,至少可以实现第一个要求。会很高兴看到“重型”机器,例如高级多态性、类型类、CanBuildFrom 等。

谢谢!

【问题讨论】:

  • 虽然这不是完全的目标,但您应该能够按照stackoverflow.com/questions/5410846 的答案完成您想要的。但是,我建议您不要使用直接递归,因为您将不得不制作大量可笑的构建器来这样做。与构建器一起递归的辅助函数可以解决问题。

标签: generics scala types scala-collections generic-programming


【解决方案1】:

要清除我的评论,这就是你要做的(没有给出解释——请参阅this question 的答案)。

def LCS[A,C](a: C, b: C)(
  implicit c2i: C => Iterable[A], cbf: collection.generic.CanBuildFrom[C,A,C]
): C = {
  val builder = cbf()
  def ListLCS(a: Iterable[A], b: Iterable[A]): List[A] = {
    if (a.isEmpty || b.isEmpty) Nil
    else if (a.head==b.head) a.head :: ListLCS(a.tail,b)
    else {
      val case1 = ListLCS(a.tail, b)
      val case2 = ListLCS(a, b.tail)
      if (case1.length > case2.length) case1 else case2
    }
  }
  builder ++= ListLCS( c2i(a), c2i(b) )
  builder.result()
}

可以直接在内部函数中使用构建器,但您必须重新设计算法;实际上,您将项目添加到列表的头部,而构建器添加到末尾。所以,为了保持算法不变,我们做了一个列表作为中间体。

【讨论】:

  • 谢谢雷克斯!你精彩的explanation“拉皮条”scala 集合正是我想知道的。必须阅读所有有兴趣将自己的在集合上操作的通用方法与 scala 集合框架集成的人。
【解决方案2】:

a.companion.empty 替换Seq.empty 给了我一个具有这种行为的函数:

scala> LCS(Vector(1, 2, 1, 2, 3), Vector(1, 1, 3))
res3: Seq[Int] = Vector(1, 1, 3)

scala> LCS(List(1, 2, 1, 2, 3), List(1, 1, 3))
res4: Seq[Int] = List(1, 1, 3)

scala> LCS("BANANA", "ANA")                       
res5: Seq[Char] = Vector(A, N, A)

scala> LCS(Array(1, 2, 1, 2, 3), Array(1, 1, 3))
res6: Seq[Int] = ArrayBuffer(1, 1, 3)

【讨论】:

  • 这只是部分解决方案。如您所见,每种情况下的编译时类型都是 Seq[T]。此外,它不适用于正确的字符串和数组。请参阅上面 Rex Kerr 的回答,他完全解决了问题。
猜你喜欢
  • 2015-09-15
  • 1970-01-01
  • 2016-07-08
  • 1970-01-01
  • 2013-08-30
  • 1970-01-01
  • 2011-09-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多