Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

recursion in groovy (grails)

Tags:

grails

groovy

I am trying to use recursion in groovy to traverse a tree relationship. The below code runs one cycle, upto childNodes && recurClosure(childNodes ) , but doesn't call the closure recurClosure again. At that instant childNodes had two objects (array list) same type as root.

In the code, recurClosure is defined and calls with a list of objects (root). It then iterates through each element and fines the child nodes (uses grails dsl for this).If the childNodes is not null, it recursively calls the parent method.

Should I break it up, or what is wrong?

def parentval 
def root = Domain.list()

def recurClosure
recurClosure = {inroot ->
  inroot.each {
    returnList << it
    parentval = it
    childNodes = Domain.withCriteria {
      eq('parent', parentval )
    }
  }
  childNodes && recurClosure(childNodes )
}(root)

return returnList

}

thanks in advance.

Update: The following exception is noted

    ERROR [2010-06-24 08:20:04,742] [groovy.grails.web.errors.GrailsExceptionResolver] Cannot invoke method call() on null object
java.lang.NullPointerException: Cannot invoke method call() on null object
    at com.bsr.test.DomainService$_closure2_closure7.doCall(com.bsr.test.DomainService:68)
    at com.bsr.test.DomainService$_closure2.doCall(com.bsr.test.DomainService:58)
    at com.bsr.test.DomainController$_closure3.doCall(DomainController.groovy:45)
    at com.bsr.test.DomainController$_closure3.doCall(DomainController.groovy)
    at org.apache.shiro.web.servlet.ShiroFilter.executeChain(ShiroFilter.java:687)
    at org.apache.shiro.web.servlet.ShiroFilter.doFilterInternal(ShiroFilter.java:616)
    at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:81)
    at java.lang.Thread.run(Thread.java:619)

Update 2: Now trying Daniel's suggestion.

{ inroot ->
    inroot.each {
        returnList << it
        parentval = it
        childNodes = Domain.withCriteria {
            eq('parent', parentval )
        }
           if(childNodes)
           call(childNodes)
    }
       /*if(childNodes)
        call(childNodes)*/

}(root)

In the above implementation, root is an arraylist, The inner closure takes each element out of it and recursively calls the anonymous closure. When I moved the 'call' inside the each closure, it doesn't call the outer anonymous closure, but the inroot.each {} itself. So, I get an error

ERROR [2010-06-24 08:47:46,438] [groovy.grails.web.errors.GrailsExceptionResolver] instance not of expected entity type: java.util.ArrayList is not a: com.bsr.test.Domain

I see a blog post about how to name the closure through 'this' > I'll update my finding.. thanks

Update 3: The way to call the outer closure is owner.call(childNodes)

like image 423
bsr Avatar asked Dec 17 '22 23:12

bsr


2 Answers

The problem is, that by

recurClosure = {
    [...]
}(root)

you don't assign the closure to recurClosure, but rather the return value of its invocation! Thus, of course, you can't call the closure via recurClosure()...

Two possible solutions:

First define the closure, and then call it separately, as air_blob suggested:

def recurClosure = {
    [...]
}
recurClosure(root)

Use the implicit call() function for recursion. This way you can even work with an anonymous closure. IMHO a very nice way to implement recursion in Groovy:

{ inroot ->
    inroot.each {
        returnList << it
        parentval = it
        childNodes = Domain.withCriteria {
            eq('parent', parentval )
        }
    }
    if(childNodes)
        call(childNodes)
}(root)

Two more comments on your code:

  1. You may want to declare returnList: def returnList = []
  2. While childNodes && recurClosure(childNodes ) may do what you want, it's much more readable to sacrifice one more char and spell out the if.. ;-)
  3. Don't you want to recursively call your closure inside the each?

Another (higher-level) remark on your code: If the parents and their children are of the same type (Domain), won't Domain.list() actually return all children, too? Is there really a need for traversing the tree manually?

like image 165
Daniel Rinser Avatar answered Jan 12 '23 01:01

Daniel Rinser


Do you get any exceptions? Have you tried invoking it separately like this:

def recurClosure
recurClosure = {inroot ->
  [... stuff ...]
}

recurClosure(root)

What exactly do you want to do in this line:

childNodes && recurClosure(childNodes )
like image 39
codeporn Avatar answered Jan 11 '23 23:01

codeporn