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 t开发者_如何学运维he 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:
- You may want to declare
returnList
:def returnList = []
- While
childNodes && recurClosure(childNodes )
may do what you want, it's much more readable to sacrifice one more char and spell out theif
.. ;-) - 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?
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 )
精彩评论