I admit that I’m sceptical of zip
because it seems (to me) like a hack to get around the limitation that arity of method calls in Scala must be known at compile time. I suspect my scepticism is misguided, but don’t really know how to verify my suspicion.
To avoid using zip
I often use a tail-recursive local function which simultaneously traverses both Lists.
Here is an example. Can someone suggest a better approach? I’m happy to have suggestions. I admit that my implementation is obscure, and every time I look at it again, I have to convince myself again that it works.
The function recursively traverses two Lists, clause1
and clause2
. At each iteration it looks at the heads of the two lists. If they are not equal in absolute value, the recursion stops and false
is returned, otherwise, the recursion continues. As soon as more than one such difference is detected, the recursion stops and false
is returned. If the end of the lists are reached, then true
or false
is returned depending on whether exactly 1 difference was discovered.
I think I could write this function in a less obfuscated way using zip
and count
, but 1) I’m afraid zip
might create an intermediate list which is unnecessary, and 2) I never want to count higher than 2. As soon as 2 is reached, I want count
to abort and qmCompatible_?
should return false
.
This is actually important for my application, as the lists might be very long, and there might be a large number of such lists which are being tested.
type Clause = List[Int]
def qmCompatible_?(clause1: Clause, clause2: Clause): Boolean = {
// We assume this function is only called with lists of the same
// length, if it is called with lists of different lengths, the
// results are undefined.
// Given two lists of Int (Clause), determine whether they
// are equal, except for exactly one pair of corresponding entries
// which are equal in absolute value.
// E.g., List(1, -2, 3) == List(1, 2, 3)
// but List(-1, -2, 3) != List(1, 2, -3)
// and List(1, 2, 4) != List(1, 2, 3)
def loop(clause1: Clause, clause2: Clause, diff: Int): Boolean = {
if (diff > 1) false
else (clause1, clause2) match {
case (a :: as, b :: bs) =>
equalAbs(a, b) && loop(as, bs, if (a == b) diff else diff + 1)
case (_, _) => 1 == diff
}
}
loop(clause1, clause2, diff=0)
}
def equalAbs(x: Int, y: Int): Boolean = {
abs(x) == abs(y)
}