I wrote the function to find longest common subsequence (LCS). For example, for two sequences of chars BANANA and ATANA it returns AANA. Implementation is naive inefficient adaptation of recursive algorithm, but it is irrelevant for purpose of this question.
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
}
}
I want to refactor this function in most generic way possible. Current implementation works for any types of input sequences, but always returns collection of type List[T]. I want to achieve following behaviour:
LCS(List('B','A','N','A','N','A'), List('A','T','A','N','A')) -> List('A','A','N','A') LCS(Vector('B','A','N','A','N','A'), Vector('A','T','A','N','A')) -> Vector('A','A','N','A') ...and so on for all other Seqs...
It would be wonderful if LCS could also handle Strings and Arrays:
LCS("BANANA", "ATANA") -> "AANA" LCS(Array('B','A','N','A','N','A'), Array('A','T','A','N','A')) -> Array('A','A','N','A')
I believe with help of Scala 2.8 generic collection library it's possible to achieve at least first requirement. Will be glad to see "heavy" machinery like higher-kinded polymorphism, type classes, CanBuildFrom and so on.
Thanks!
To flush out my comment, here is what you'd do (no explanation given--for that, see the answer to 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()
}
It is possible to use a builder directly inside the inner function, but you'd have to rework the algorithm; as it is, you add items to the head of the list, whereas builders add to the end. So, in order to keep the same algorithm, we make a list as an intermediate.
Replacing Seq.empty
with a.companion.empty
gave me a function with this behavior:
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With