为什么这个 Groovy 闭包没有返回我期望的值?
在 Grails 应用程序中,我试图防止在有向图中创建循环。用户可以将父节点分配给节点,但任何节点都不应是其父节点的祖先。我编写了一个简单的设置函数,它调用 checkLineageForTarget,这是执行繁重任务的递归函数:
boolean checkLineageForTarget(Integer target, Collection<Node>stillToProcess){
// true means that this is a safe addition
// false means that this addition creates a cycle
boolean retVal = stillToProcess.each {
Collection<Node> itsParents = getParentNodes(it)
if (it.id == target){
println("found a loop on " + target);
return false; // loop detected!
}
if (itsParents.empty){ return true; } // end of the line
return checkLineageForTarget(target, itsParents)
}
// at this point, retVal is always true, even when the "found a loop [...]" condition is met
return retVal;
}
这个“有效”,因为它打印“找到一个循环 [...]”消息,但在闭包之外, retVal 为 true,调用函数尝试添加新的父/子关系,并且我的堆栈溢出。
我的误解是什么?
In a Grails application, I am trying to prevent the creation of cycles in a directed graph. The user is able to assign a parent to a node, but no node should be its own parent's ancestor. I have written a simple setup function that calls checkLineageForTarget, which is the recursive function that does the heavy lifting:
boolean checkLineageForTarget(Integer target, Collection<Node>stillToProcess){
// true means that this is a safe addition
// false means that this addition creates a cycle
boolean retVal = stillToProcess.each {
Collection<Node> itsParents = getParentNodes(it)
if (it.id == target){
println("found a loop on " + target);
return false; // loop detected!
}
if (itsParents.empty){ return true; } // end of the line
return checkLineageForTarget(target, itsParents)
}
// at this point, retVal is always true, even when the "found a loop [...]" condition is met
return retVal;
}
This "works," in that it prints the "found a loop [...]" message, but outside of the closure, retVal is true, the calling function attempts to add the new parent/child relationship, and my stack runneth over.
What is my misunderstanding?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
每个
方法返回与调用它的集合相同的集合,因此 retVal
可能不是布尔值“true”,而是被评估为“truthly”(因为它是一个集合,所以它会意味着它不为空)。如果要检查集合中每个元素的条件,可以使用
每个
。请注意,我不需要检查父节点集合上的
.empty
条件,因为它将通过对 checkLineageForTarget 的递归调用进行过滤(即在空节点上调用.every
集合始终返回 true)。另外,由于&&
运算符的短路,一旦node.id == target
迭代就会停止:)The
each
method returns the same collection it was invoked on, soretVal
is probably not the boolean "true", but is evaluated as "truthly" (as it is a collection, it would mean it's not empty).If you want to check a condition for every element in a collection, you might use
every
.Note that I didn't need check the
.empty
condition on the parent nodes collection because that will be filtered by the recursive call to checkLineageForTarget (i.e. calling.every
on an empty collection always returns true). Also, because of the short-circuiting of the&&
operator, the iteration stops as soon asnode.id == target
:).each
似乎在完成时返回正在循环的对象。您将其分配给一个布尔值,并且它被强制为true
。您可能希望使用.every
来完成您的任务。仅当每次迭代返回true
时,它才返回true
,并且当遇到第一个false
时,它将停止循环。您可以在 groovy 文档中找到更多信息。.each
appears to return the Object being looped over when it is done. You are assign this to a boolean and it is being coerced totrue
. You probably want to use.every
for your task. It returnstrue
only if each iteration returnstrue
and it will stop looping when it hits the firstfalse
. You can find more information in the groovy docs.当您返回闭包内部时,就像返回方法内的方法调用 - 它是该范围的本地方法,并且对调用闭包的实际方法没有影响。在这种情况下,您可以使用建议的其他方法之一(例如 every)或使用常规 for 循环,因为它的工作方式与 Groovy every 相同(即它是空安全的并且支持但不需要类型),但您可以跳出循环或返回,因为您处于一个真正的 for 循环,它将返回方法:
When you return inside a Closure, it's like returning inside a method call within the method - it's local to that scope and has no impact on the real method the closure is being called in. In this case you can use one of the other approaches suggested (e.g. every) or use a regular for loop since it works the same as the Groovy each (i.e. it's null-safe and supports but doesn't require types) but you can break out of the loop or return and since you're in a real for loop it will return from the method: