【问题标题】:Tree Traversal via Blocks with a premature stopping condition通过具有过早停止条件的块进行树遍历
【发布时间】:2017-02-25 04:31:41
【问题描述】:

我正在使用在每个节点上执行的特定方法对二叉树进行按顺序遍历。我使用如下所示的inOrderTraversalWithOperation: 方法执行此操作,该方法使用块来定义每个节点所需的函数。

-(void) inOrderTraversalWithOperation:(void (^) (BinaryTreeNode *))operation
{
    [self.leftChild inOrderTraversalWithOperation:operation];
    if (operation)
    {
        operation(self);
    }
    [self.rightChild inOrderTraversalWithOperation:operation];

}

假设我想在 Block 执行达到某个条件时停止。一种方法是让inOrderTraversalWithOperation: 返回BOOL,并使Block 返回BOOL,如下所示。

但我想知道我是否可以使用 Apple 在其许多 API 中使用的 BOOL *stop 方法来做到这一点。带有该标志的块如何“在下面”工作?

-(BOOL) inOrderTraversalWithStopOperation:(BOOL (^) (BinaryTreeNode *))operation
{
    BOOL shouldStop = NO;

    shouldStop = [self.leftChild inOrderTraversalWithStopOperation:operation];
    if (operation !=nil && shouldStop == NO)
    {
        shouldStop = operation(self);
    }

    if (!shouldStop)
    {
        shouldStop = [self.rightChild inOrderTraversalWithStopOperation:operation];
    }

    return shouldStop;
}

编辑 根据 Josh 的评论,BOOL *stop 似乎允许这样做,但我仍然需要 inOrderTraversalWithStopOperation: 来返回 BOOL

-(BOOL) inOrderTraversalWithStopOperation:(void (^) (BinaryTreeNode *, BOOL *))operation
{
    BOOL shouldStop = NO;

    shouldStop = [self.leftChild inOrderTraversalWithStopOperation:operation];
    if (operation !=nil && shouldStop == NO)
    {
        operation(self, &shouldStop);
    }

    if (!shouldStop)
    {
        shouldStop = [self.rightChild inOrderTraversalWithStopOperation:operation];
    }

    return shouldStop;
}

【问题讨论】:

    标签: objective-c tree objective-c-blocks


    【解决方案1】:

    枚举块的“停止”参数就像任何其他间接返回值一样:您从一个范围传递一个 地址,以便下一个范围可以在那里放置一些东西。然后在原始范围内可以使用该东西。

    要为您的操作类型添加停止标志,您需要更改其签名

    typedef void (^NodeOperation)(BinaryTreeNode *, BOOL *);
    

    在 Block 的调用上下文中,您执行您已经完成的操作:为此标志创建一个 BOOL 并将其设置为 NO。然后你将它的地址传递给区块:operation(self, &stop);

    在操作内部,如有必要,您可以通过取消引用它并分配一个值来设置标志:*stop = YES;(最好先检查它不是 NULL;取消引用 NULL 是非法的)。

    回到控制范围,做你已经在做的事情:在每次操作后检查标志并决定做什么。

    在我对What is the BOOL *stop argument for enumerateObjectsUsingBlock: used for? 的相关回答中有此机制的代码示例

    要控制递归方法调用,您必须以某种方式将信息传回。您可以使用直接返回值最轻松地做到这一点。另一种选择(尽管在这种情况下我认为它不会给您带来任何好处)是在方法中添加一个BOOL 指针参数;然后你可以继续传递相同的引用到每个级别。 (这可能意味着创建一个辅助方法,以便原始调用者不必担心该参数。)

    【讨论】:

    • 谢谢乔希。我可以清楚地看到如何知道块是否导致当前节点的停止条件。我无法得到的是如何知道在左孩子和右孩子上调用该方法时满足停止条件。看起来我们需要“遍历方法”来返回 BOOL。即 BOOL *stop 只会帮助消除方法的完成块参数的 BOOL 返回类型,但我们仍然需要方法的返回类型为 BOOL。那是对的吗?请参阅上面我编辑的问题。
    • 是的,任何给定的调用都无法影响以前的调用,除非直接返回传递信息。或者,如果你想——我认为它不会在这里为你买任何东西——你也可以在方法中添加一个 BOOL * 参数,并以相同的方式将原始 BOOL 传递给每个方法调用(即[self.leftChild inOrderTraversalWithStop:&shouldStop operation:operation] 或类似名称)。这可能意味着创建一个辅助方法以避免原始调用者必须传入布尔指针。
    • 谢谢,您可以添加您的答案以便我接受吗?
    • 当然;已添加。
    猜你喜欢
    • 2020-09-16
    • 2012-06-15
    • 2014-02-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-04
    • 2018-06-09
    相关资源
    最近更新 更多