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)
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:
returnList
: def returnList = []
childNodes && recurClosure(childNodes )
may do what you want, it's much more readable to sacrifice one more char and spell out the if
.. ;-)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?
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 )
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