为什么这个 Groovy 闭包没有返回我期望的值?

发布于 2025-01-08 10:02:06 字数 918 浏览 2 评论 0原文

在 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

青衫儰鉨ミ守葔 2025-01-15 10:02:06

每个方法返回与调用它的集合相同的集合,因此 retVal 可能不是布尔值“true”,而是被评估为“truthly”(因为它是一个集合,所以它会意味着它不为空)。

如果要检查集合中每个元素的条件,可以使用 每个

boolean checkLineageForTarget(Integer target, Collection<Node>stillToProcess){
    stillToProcess.every { node ->
        node.id != target && checkLineageForTarget(target, getParentNodes(node))
    }
}

请注意,我不需要检查父节点集合上的 .empty 条件,因为它将通过对 checkLineageForTarget 的递归调用进行过滤(即在空节点上调用 .every集合始终返回 true)。另外,由于 && 运算符的短路,一旦 node.id == target 迭代就会停止:)

The each method returns the same collection it was invoked on, so retVal 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.

boolean checkLineageForTarget(Integer target, Collection<Node>stillToProcess){
    stillToProcess.every { node ->
        node.id != target && checkLineageForTarget(target, getParentNodes(node))
    }
}

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 as node.id == target :)

入画浅相思 2025-01-15 10:02:06

.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 to true. You probably want to use .every for your task. It returns true only if each iteration returns true and it will stop looping when it hits the first false. You can find more information in the groovy docs.

梦幻的心爱 2025-01-15 10:02:06

当您返回闭包内部时,就像返回方法内的方法调用 - 它是该范围的本地方法,并且对调用闭包的实际方法没有影响。在这种情况下,您可以使用建议的其他方法之一(例如 every)或使用常规 for 循环,因为它的工作方式与 Groovy every 相同(即它是空安全的并且支持但不需要类型),但您可以跳出循环或返回,因为您处于一个真正的 for 循环,它将返回方法:

boolean checkLineageForTarget(Integer target, Collection<Node>stillToProcess){

   for (Node node in stillToProcess) {
      Collection<Node> itsParents = getParentNodes(node)
      ...
   }
   ...
}

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:

boolean checkLineageForTarget(Integer target, Collection<Node>stillToProcess){

   for (Node node in stillToProcess) {
      Collection<Node> itsParents = getParentNodes(node)
      ...
   }
   ...
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文