5

I was given a question to compare two trees. Something like below:

case class Node(elem:String, child:List[Node])

In order to compare each elements of the trees, I have following functions:

def compare(n1:Node, n2:Node): Boolean {
   if(n1.elem == n2.elem){
      return compare(n1.child, n2.child)
   }
}

def compare(c1:List[Node], c2:List[Node]): Boolean {
    while (c1.notEmpty) {
       //filter, map etc call function above to compare the element recursively
    }
}

Basically algorithm is for each elements in n1, we are checking for a match in n2. I was told that this is very imperative way and not functional way. What would be a functional way to achieve this behaviour. In other words, how do we remove while loop when comparing the list of children?

user_1357
  • 7,766
  • 13
  • 63
  • 106
  • Do you need to compare every element in the first list with every element in the second list? Or just both first elements, both seconds elements and so on? – fresskoma Aug 26 '14 at 20:59
  • Yes, I want to compare two unordered children to see weather one list contains exactly the same elements as the other all the way down to the leaf nodes. – user_1357 Aug 27 '14 at 01:42

2 Answers2

4

Consider zipping both lists and using forall which holds true only if each and every predicate it processes evaluates to true; for instance like this,

def compare(c1: List[Node], c2: List[Node]): Boolean = 
  (c1.sorted zip c2.sorted).forall(c => compare(c._1,c._2))

Note that forall will halt the evaluations as it encounters the first false. Cases of unequal length lists of nodes may be tackled with zipAll by defining an EmptyNode class for padding length differences; also both lists empty may compare to true.

Update Sorted lists of nodes for soundness following comment by @JohnB.

samthebest
  • 30,803
  • 25
  • 102
  • 142
elm
  • 20,117
  • 14
  • 67
  • 113
  • Maybe I'm misunderstanding the question, but it seems like the OP wants to compare every node in the first list to every other node in the second list. Your code would not achieve that, am I right? – fresskoma Aug 26 '14 at 20:58
  • 1
    With the zipAll and a 'emptynode' instance to pad any missing cases it would compare them all. The issue I see however, is that you could consider them equal if they had all the same nodes but different ordering, so sorting first or converting both lists to sets and doing set1.forall(c => s2.contains(c)) and the inverse. – JohnB Aug 26 '14 at 22:40
  • @JohnB agree, Many Thanks for the comments. – elm Aug 27 '14 at 01:44
  • Um, I think Seq already has equals that compares all elements. You could use `List.corresponds` for other predicates. BTW, OP said children are unordered, but that doesn't sound normal. (sorry, I saw the OP comment "exactly the same elements", which isn't the general compare.) – som-snytt Aug 27 '14 at 02:22
  • zip won't work in my case, I need to compare each element of outer list to each element in inner list. Combining nodes at same position won't work. – user_1357 Sep 01 '14 at 14:06
2

If I understood your question correctly, you want to compare every element of the first list with every element of the second list. The following code achieves this. It gets rid of the while loop via a tail-recursion.

import scala.annotation.tailrec

def cmp(a:Int, b:Int) = a > b

@tailrec
def compare(xs: List[Int], ys: List[Int]): Boolean = xs match {
    case Nil => true
    case head :: tail if ys.forall(x => cmp(head, x)) => compare(tail, ys)
    case _ => false
}
samthebest
  • 30,803
  • 25
  • 102
  • 142
fresskoma
  • 25,481
  • 10
  • 85
  • 128