使用 "Long zip with" 的惰性 Python 中的树级别

Tree levels in lazy Python using "Long zip with"

this关于广度优先遍历的博客post中,Gibbons谈到用“long zip with”函数实现BFT

lzw :: (a -> a -> a) -> [a] -> [a] -> [a]
lzw f x:xs y:ys = f x y : lzw f xs ys
lzw _ [] ys = ys
lzw _ xs [] = xs

那么比如说

data Tree a = EmpT | TN a (Tree a) (Tree a)
levels :: Tree a -> [[a]]
levels ET = []
levels (TN x tl tr) = [x] : lzw (++) (levels tl) (levels tr)

My question: My version of levels in Python is working, but my lazy version using generators is not. It's adding an extra [] level at the end. Why?

我认为这种使用生成器的做法和 Python 中的惰性,我认为问题在于我使用 yieldreturn、[=20 的方式有些不精确=],等等

错误示例:

>>> my_tree = tree_from_int_segment(10)
>>> pprint(my_tree)

{'left': {'left': {'left': {'left': None, 'right': None, 'val': 0},
                   'right': None,
                   'val': 1},
          'right': {'left': {'left': None, 'right': None, 'val': 3},
                    'right': None,
                    'val': 4},
          'val': 2},
 'right': {'left': {'left': {'left': None, 'right': None, 'val': 6},
                    'right': None,
                    'val': 7},
           'right': {'left': None, 'right': None, 'val': 9},
           'val': 8},
 'val': 5}

>>> tree_levels(my_tree)

[[5], [2, 8], [1, 4, 7, 9], [0, 3, 6]]

>>> list(tree_levels_lazy(my_tree))

[[5], [2, 8], [1, 4, 7, 9], [0, 3, 6], []]

这是我的代码。提前致谢!

from itertools import islice
from itertools import zip_longest


def conditional_apply(g, x, y):
    if x is None:
        return y
    elif y is None:
        return x
    else:
        return g(x, y)


def long_zip_with(f, xs, ys):
    zs = list(zip_longest(xs, ys))
    return [conditional_apply(f, x, y) for x, y in zs]


def long_zip_with_lazy(f, xs, ys):
    zs = zip_longest(xs, ys)
    for x, y in zs:
        yield conditional_apply(f, x, y)


def tree_levels(t):
    if t is None:
        return []

    a = t["val"]
    tl = t["left"]
    tr = t["right"]
    return [[a]] + long_zip_with(list.__add__, tree_levels(tl), tree_levels(tr))


def tree_levels_lazy(t):
    if t is None:
        yield []
        return

    a = t["val"]
    tl = t["left"]
    tr = t["right"]
    first_level = [a]

    yield first_level
    for level in long_zip_with_lazy(list.__add__, tree_levels_lazy(tl), tree_levels_lazy(tr)):
        yield level

# Rest is just code for building a tree to work with


def split_at(k, xs):
    it = iter(xs)
    l = list(islice(it, k))
    r = list(it)
    return l, r


def split_list_for_tree(xs):
    if not xs:
        return None
    n = len(xs)
    xsl_p, xsr = split_at(n//2 + 1, xs)
    x = xsl_p.pop()
    return x, xsl_p, xsr


def unfold_tree(seed_to_sprout, init_value):

    def unfolder(seed):
        sprout = seed_to_sprout(seed)
        if sprout is None:
            return None
        x, seed_l, seed_r = sprout
        return {"val": x,
                "left": unfolder(seed_l),
                "right": unfolder(seed_r)}

    return unfolder(init_value)


def tree_from_int_segment(n):
    return unfold_tree(split_list_for_tree, list(range(n)))

由于生成器版本不连接子列表,因此不应生成空列表:

def tree_levels_lazy(t):
    if t is None:
        #yield []  <-- remove this
        return
    ...