2010-06-24 7 views
4

J'essaie d'utiliser la récursivité dans groovy pour traverser une relation d'arbre. Le code ci-dessous exécute un cycle, jusqu'à childNodes & & recurClosure (childNodes), mais n'appelle pas la fermeture recurClosure à nouveau. A ce moment, childNodes avait deux objets (liste de tableaux) du même type que root.récursivité dans groovy (grails)

Dans le code, recurClosure est défini et appelle avec une liste d'objets (root). Il parcourt ensuite chaque élément et infléchit les nœuds enfants (utilise grails dsl pour cela). Si childNodes n'est pas nul, il appelle récursivement la méthode parent.

Dois-je le casser, ou quel est le problème?

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 

}

merci à l'avance.

Mise à jour: L'exception suivante est notée

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) 

Mise à jour 2: Maintenant, essayer la suggestion de Daniel.

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

}(root) 

Dans la mise en œuvre ci-dessus, la racine est un arraylist, la fermeture intérieure prend chaque élément sur et appelle récursivement la fermeture anonyme. Lorsque j'ai déplacé le 'call' à l'intérieur de chaque fermeture, il n'appelle pas la fermeture anonyme externe, mais inroot.each {} lui-même. Donc, je reçois une erreur

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 

Je vois un billet de blog sur la façon de nommer la fermeture par 'this'> Je vais mettre à jour ma conclusion .. merci

Mise à jour 3: La façon d'appeler la fermeture extérieure est owner.call (childNodes)

Répondre

10

Le problème est que par

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

vous n n'attribuez pas la fermeture à recurClosure, mais plutôt à la valeur de retour de son invocation! Ainsi, bien sûr, vous ne pouvez pas appeler la fermeture par recurClosure() ...

Deux solutions possibles:

d'abord définir la fermeture, puis l'appeler séparément, comme air_blob suggéré:

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

Utilisez la fonction implicite call() pour la récursivité. De cette façon, vous pouvez même travailler avec une fermeture anonyme.À mon humble avis une très belle façon de mettre en œuvre récursion dans Groovy:

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

Deux autres commentaires sur votre code:

  1. Vous pouvez déclarer returnList: def returnList = []
  2. Alors que childNodes && recurClosure(childNodes) peut faire ce que vous voulez, il est beaucoup plus lisible de sacrifier un char de plus et d'épeler le if .. ;-)
  3. Ne voulez-vous pas appeler récursivement votre fermeture à l'intérieur le each?

Un autre (niveau supérieur) remarque sur votre code: Si les parents et leurs enfants sont du même type (Domain), ne Domain.list() retourneront tous les enfants aussi? Est-il vraiment nécessaire de traverser l'arbre manuellement?

+0

Daniel .. heureux de voir votre réponse, car il attrape toutes les erreurs dans le code .. Eh bien, j'ai adapté ma liste de code pour le rendre plus simple pour la question. Donc, 1. J'ai défini returnList comme une liste de tableaux. 2. Je suis d'accord 3. Vous avez raison, ce que je voulais (j'ai essayé de déplacer la déclaration dans et hors de cette fermeture pour voir l'effet). Je n'étais certainement pas en train de faire un list() sur la classe du domaine, en effet, je l'ai obtenu d'une autre table. Je n'ai jamais eu l'intention de tromper un expert comme vous (à la fois dans groovy et grails: - |) .. J'essaie votre mise en œuvre de fermeture anonyme .. mettra à jour dans un peu .. merci beaucoup – bsr

+0

Wow, excellente réponse +1 – codeporn

+0

Daniel , J'ai mis à jour la question avec plus de détails .. maintenant la question est, comment invoquer la fermeture anonyme externe par «appel». Merci encore. – bsr

2

Avez-vous des exceptions? Avez-vous essayé d'invoquer séparément comme ceci:

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

recurClosure(root) 

Que voulez-vous voulez faire dans cette ligne:

childNodes && recurClosure(childNodes) 
+0

Merci pour les suggetions .. J'ai mis à jour avec l'exception. L'intention de childNodes && recurClosure (childNodes) était de continuer la récursivité jusqu'à ce que childnode soit null. Peut être la liste de tableau ne sera pas nulle, donc j'ai essayé (childNodes.size()> 0) && recurClosure (childNodes) mais le même résultat. De l'exception, il semble qu'il ne peut pas accéder à la fermeture. – bsr

Questions connexes