Skip to content

Commit 4cb5320

Browse files
committed
replace some recursion with foldLeft
1 parent f5e2508 commit 4cb5320

File tree

1 file changed

+30
-47
lines changed

1 file changed

+30
-47
lines changed

src/main/scala/gnieh/diff/Patience.scala

Lines changed: 30 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@
1414
package gnieh.diff
1515

1616
import scala.annotation.tailrec
17-
18-
import scala.collection.SeqView
17+
import scala.collection._
1918

2019
/** Implementation of the patience algorithm [1] to compute the longest common subsequence
2120
*
@@ -34,38 +33,20 @@ class Patience[T](withFallback: Boolean = true) extends Lcs[T] {
3433
type Occurrence = (T, Int)
3534

3635
/** Returns occurrences that appear only once in the list, associated with their index */
37-
private def uniques(l: SeqView[T, IndexedSeq[T]]): List[Occurrence] = {
38-
@tailrec
39-
def loop(idx: Int, acc: Map[T, Int]): List[Occurrence] =
40-
if (idx >= l.size) {
41-
acc.toList
42-
} else {
43-
val value = l(idx)
44-
if (acc.contains(value)) {
45-
// not unique, remove it from the accumulator and go further
46-
loop(idx + 1, acc - value)
47-
} else {
48-
loop(idx + 1, acc.updated(value, idx))
49-
}
50-
}
51-
loop(0, Map())
52-
}
36+
private def uniques(l: SeqView[T, IndexedSeq[T]]): List[Occurrence] =
37+
l.zipWithIndex.foldLeft(Map.empty[T, Int]) {
38+
case (acc, (value, idx)) =>
39+
// if not unique, remove it from the accumulator and go further
40+
if (acc.contains(value)) acc - value else acc.updated(value, idx)
41+
}.toList
5342

5443
/** Takes all occurences from the first sequence and order them as in the second sequence if it is present */
5544
private def common(l1: List[Occurrence], l2: List[Occurrence]): List[(Occurrence, Int)] = {
56-
@tailrec
57-
def loop(l: List[Occurrence], acc: List[(Occurrence, Int)]): List[(Occurrence, Int)] = l match {
58-
case occ :: tl =>
59-
// find the element in the second sequence if present
60-
l2.find(_._1 == occ._1) match {
61-
case Some((_, idx2)) => loop(tl, (occ -> idx2) :: acc)
62-
case None => loop(tl, acc)
63-
}
64-
case Nil =>
65-
// sort by order of appearance in the second sequence
66-
acc sortWith (_._2 < _._2)
67-
}
68-
loop(l1, Nil)
45+
val l2Indices: Map[T, Int] = l2.map { case (t, idx) => t -> idx }(breakOut)
46+
l1.foldLeft(List.empty[(Occurrence, Int)]) {
47+
case (acc, occ @ (t, _)) =>
48+
l2Indices.get(t).fold(acc)(idx => (occ -> idx) :: acc)
49+
}.sortBy(_._2)
6950
}
7051

7152
/** Returns the list of elements that appear only once in both l1 and l2 ordered as they appear in l2 with their index in l1 */
@@ -83,26 +64,28 @@ class Patience[T](withFallback: Boolean = true) extends Lcs[T] {
8364
if (l.isEmpty) {
8465
Nil
8566
} else {
86-
@tailrec
87-
def push(idx1: Int, idx2: Int, stacks: List[List[Stacked]], last: Option[Stacked], acc: List[List[Stacked]]): List[List[Stacked]] = stacks match {
88-
case (stack @ (Stacked(idx, _, _) :: _)) :: tl if idx > idx1 =>
89-
// we found the right stack
90-
acc.reverse ::: (Stacked(idx1, idx2, last) :: stack) :: tl
91-
case (stack @ (stacked :: _)) :: tl =>
92-
// try the next one
93-
push(idx1, idx2, tl, Some(stacked), stack :: acc)
94-
case Nil =>
95-
// no stack corresponds, create a new one
96-
acc.reverse ::: List(List(Stacked(idx1, idx2, last)))
97-
case Nil :: _ =>
98-
// this case should NEVER happen
99-
throw new Exception("No empty stack must exist")
100-
}
67+
10168
def sort(l: List[(Occurrence, Int)]): List[List[Stacked]] =
10269
l.foldLeft(List[List[Stacked]]()) {
10370
case (acc, ((_, idx1), idx2)) =>
104-
push(idx1, idx2, acc, None, Nil)
71+
72+
@tailrec
73+
def push(stacks: List[List[Stacked]], last: Option[Stacked], acc: List[List[Stacked]]): List[List[Stacked]] =
74+
stacks match {
75+
case (stack @ (Stacked(idx, _, _) :: _)) :: tl if idx > idx1 =>
76+
// we found the right stack
77+
acc.reverse ::: (Stacked(idx1, idx2, last) :: stack) :: tl
78+
case (stack @ (stacked :: _)) :: tl =>
79+
// try the next one
80+
push(tl, Some(stacked), stack :: acc)
81+
case Nil =>
82+
// no stack corresponds, create a new one
83+
acc.reverse ::: List(List(Stacked(idx1, idx2, last)))
84+
}
85+
86+
push(acc, None, Nil)
10587
}
88+
10689
val sorted = sort(l)
10790
// this call is safe as we know that the list of occurrence is not empty here and that there are no empty stacks
10891
val greatest = sorted.last.head

0 commit comments

Comments
 (0)