Recursive to Iterative conversion of code written in C

Viewed 125

This is code to print an xml tree in C. How to write this iteratively?

static void
print_element_names(xmlNode *a_node)
{
    xmlNode *cur_node = NULL;

    for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
        if (cur_node->type == XML_ELEMENT_NODE) {
            printf("node type: Element, name: %s\n", cur_node->name);
        }
        print_element_names(cur_node->children);
    }
}
2 Answers

You can take advantage of the parent node in the xmlNode structure used in libxml2:

typedef struct _xmlNode xmlNode;
struct _xmlNode {
    void *_private;              // application data
    xmlElementType type;         // type number, must be second !
    const xmlChar *name;         // the name of the node, or the entity
    struct _xmlNode *children;   // parent->childs link
    struct _xmlNode *last;       // last child link
    struct _xmlNode *parent;     // child->parent link
    struct _xmlNode *next;       // next sibling link
    struct _xmlNode *prev;       // previous sibling link
    struct _xmlDoc *doc;         // the containing document End of common p
    xmlNs *ns;                   // pointer to the associated namespace
    xmlChar *content;            // the content
    struct _xmlAttr *properties; // properties list
    xmlNs *nsDef;                // namespace definitions on this node
    void *psvi;                  // for type/PSVI informations
    unsigned short line;         // line number
    unsigned short extra;        // extra data for XPath/XSLT
};

Here is a non recursive version:

static void print_element_names(xmlNode *a_node) {
    if (a_node != NULL) {
        xmlNode *stop = a_node->parent;
        xmlNode *cur_node = a_node;
        int skip = 0;

        for (;;) {
            if (!skip) {
                if (cur_node->type == XML_ELEMENT_NODE) {
                    printf("node type: Element, name: %s\n", cur_node->name);
                }
                if (cur_node->children) {
                    cur_node = cur_node->children;
                    continue;
                }
            }
            if (cur_node->next) {
                cur_node = cur_node->next;
                skip = 0;
            } else {
                cur_node = cur_node->parent;
                if (cur_node == stop)
                    break;
                skip = 1;
            }
        }
    }
}

In the context of libxml2, here's a general approach that handles preorder and postorder traversal. As @chqrlie noted, libxml2 stores a pointer to the parent node, so you can iterate a subtree using constant memory.

typedef void
(*callbackFn)(xmlNode *cur, void *data);

void
iterateSubtree(xmlNode *cur,
               callbackFn callbackPreorder,
               callbackFn callbackPostorder,
               void *data) {
    xmlNode *orig;

    if (!cur)
        return;

    orig = cur;
    do {
        callbackPreorder(cur, data);

        while (cur->children) {
            cur = cur->children;
            callbackPreorder(cur, data);
        }

        while (cur != orig) {
            // Storing a copy of next and parent allows the postorder
            // callback to append to or delete a node.
            xmlNode *next = cur->next;
            xmlNode *parent = cur->parent;

            callbackPostorder(cur, data);

            if (next) {
                cur = next;
                break;
            }
            cur = parent;
        }
    } while (cur != orig);

    callbackPostorder(cur, data);
}
Related