博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Converting Recursive Traversal to Iterator
阅读量:6038 次
发布时间:2019-06-20

本文共 4418 字,大约阅读时间需要 14 分钟。

In this article, I'm going to introduce a general pattern named Lazy Iterator for converting recursive traversal to iterator. Let's start with a familiar example of inorder traversal of binary tree.

tree

It is straightforward to write a recursive function which returns the nodes as List:

// JavaList
traverseInOrder(Node node) { if (node == null) { return emptyList(); } Return concat(traverseInOrder(node.left), List.of(node), traverseInOrder(node.right));}List
concat(List
... lists) { ... // List concatenation.}

Of course, we can simply return the iterator of the result list, but the drawback is twofold: 1) it is not lazily evaluated, which means even if we only care about the first a few items, we must traverse the whole tree. 2) the space complexity is O(N), which is not really necessary. We'd like to have an iterator which is lazily evaluated and takes memory only as needed. The function signature is given as below:

// JavaIterator
traverseInOrder(Node node) { // TODO}

One idea most people could easily come up with (but not necessarily be able to correctly implement) is to use a stack to keep track of the state of traversal. That works, but it is relatively complex and not general, there is a neat, simple and general way. This is the code which demonstrates the pattern:

// JavaIterator
traverseInOrder(Node node) { if (node == null) { return emtpyIterator(); } Supplier
> leftIterator = () -> traverseInOrder(node.left); Supplier
> currentIterator = () -> singleNodeIterator(node); Supplier
> rightIterator = () -> traverseInOrder(node.right); return concat(leftIterator, currentIterator, rightIterator);}Iterator
concat(Supplier
>... iteratorSupplier) { // TODO}

If you are not familiar with Java, Supplier<T> is a function which takes no argument and returns a value of type T, so basically you can think of it as a lazily evaluated T.

Note the structural correspondence between the code above and the original recursive traversal function. Instead of traverse the left and right branches before returning, it creates a lambda expression for each which when evaluated will return an iterator. So the iterators for the subproblems are lazily created.

Finally and the most importantly, it needs the help of a general function concat which creates an iterator backed by multiple iterator suppliers. The type of this function is (2 suppliers):

Supplier<Iterator<T>> -> Supplier<Iterator<T>> -> Iterator<T>

The key is that this function must keep things lazy as well, evaluate only when necessary. Here is how I implement it:

// JavaIterator
concat(Supplier
>... iteratorSuppliers) { return new LazyIterator(iteratorSuppliers);}class LazyIterator
{ private final Supplier
>[] iteratorSuppliers; private int i = 0; private Iterator
currentIterator = null; public LazyIterator(Supplier
>[] iteratorSuppliers) { this.iteratorSuppliers = iteratorSuppliers; } // When returning true, hasNext() always sets currentIterator to the correct iterator // which has more elements. @Override public boolean hasNext() { while (i < iteratorSuppliers.length) { if (currentIterator == null) { currentIterator = iteratorSuppliers[i].apply(); } if (currentIterator.hasNext()) { return true; } currentIterator = null; i++; } return false; } @Override public T next() { return currentIterator.next(); }}

The LazyIterator class works only on Iterator<T>, which means it is orthogonal to specific recursive functions, hence it serves as a general way of converting recursive traversal into iterator.

If you are familiar with languages with generator, e.g., Python, the idiomatic way of implementing iterator for recursive traversal is like this:

# Pythondef traverse_inorder(node):  if node.left:    for child in traverse_inorder(node.left):      yield child  yield node  if node.right:    for child in traverse_inorder(node.right):      yield child

yield will save the current stack state for the generator function, and resume the computation later when being called again. You can think of the lazy iterator pattern introduced in this article as a way of capturing the computation which can be resumed, hence simulate the generator feature in other languages.

 

转载地址:http://zmrhx.baihongyu.com/

你可能感兴趣的文章
C# 加密解密(DES,3DES,MD5,Base64) 类
查看>>
弹出保存文件、打开文件对话框
查看>>
ThinkPHP 获取配置文件中的值
查看>>
js正則表達式语法
查看>>
Android 测试工具集02
查看>>
【CF】148D Bag of mice
查看>>
Wi-Fi万能钥匙:说是破解,其实有危险(转)
查看>>
微软历史最高市值是多少?
查看>>
Boost中的智能指针(转)
查看>>
EntityFramework 实体拆分和表拆分
查看>>
web应用程序访问串口
查看>>
简介数据库日志文件的增长
查看>>
Weblogic
查看>>
Don't Repeat Yourself (不要重复你自己)
查看>>
json \u unicode字符串转化 c++
查看>>
基于HTML5 SVG和CSS3炫酷蹦床式图片切换特效
查看>>
C语言指针的初始化和赋值
查看>>
AsyncTask測试多任务
查看>>
OpenGL------三维变换
查看>>
Android Studio系列教程一--下载与安装
查看>>