【问题标题】:filtering tuple inside a tuple recursively递归地过滤元组内的元组
【发布时间】:2019-01-10 15:43:10
【问题描述】:

我正在尝试弄清楚如何过滤元组中的元组 像这样:

input>>>filter_tree((1,2,3,4,(1,2,3)),lambda x:x%2==0)

output>>> ((2,4,(2))

这是我走了多远:

 def filter_tree(tree,f):
     if type(tree)!=tuple:
           return tree
     return tuple(filter(f,tuple(filter_tree(node,f) for node in tree)))  

当元组中没有元组时它可以工作,而且我不知道我需要更改什么才能使其工作(我必须只使用递归)。 有人有想法吗?

【问题讨论】:

  • 是否保证数据项中没有列表、集合或其他可迭代对象?如果没有,返回的项目是否需要重复这些结构,还是可以只使用元组?返回的项目中是否可以接受列表而不是元组? (最后一个可能会使问题更容易一些。)
  • 保证它只接受元组,不接受列表或类似的东西。是的,它必须返回一个元组

标签: python recursion filter tuples


【解决方案1】:

这是一个同样有效的生成器解决方案:

def filter_nested_tuples(iterable, f):
    """filter elements from nested tuples from function f"""
    for x in iterable:
        if isinstance(x, tuple):
            yield tuple(filter_nested_tuples(x, f))
        elif f(x):
            yield x

def is_even(x):
    """Indicates if number is even"""
    return x % 2 == 0

tup = (1,2,3,4,(1,2,3))
print(tuple(filter_nested_tuples(tup, f=is_even)))
# (2, 4, (2,))

注意:使用isinstance()而不是type()的好处是当你想支持多种类型,如list,set,那么你可以只传递一个类型的元组:isinstance(x, (list, set, tuple))。

【讨论】:

  • 我没有看到使用生成器的优势。大概是为了避免在内存中分配结构。但是当你在内部调用yield tuple(filter_nested_tuples(...)) 时,你会耗尽子生成器并分配结构。当您在最终结果上致电 tuple() 时也是如此。如果内部呼叫是yield from filter_nested_tuples(...),那么我可以从记忆中看到胜利。当然,它会破坏结构。如果目标是“按顺序”输出偶数,那么生成器显然是赢家。
  • @cdlane 与我的原始解决方案一样,这不会破坏底层递归结构,因为生成器本身正在递归使用。
  • @meowgoesthedog,你误解了我的评论。该解决方案确实保留了底层递归结构。但它消除了使用生成器这样做的任何优势。但值得注意的特点是它使用了一个生成器。
【解决方案2】:

只对非元组元素直接应用过滤函数,只对元组元素进行递归:

def filter_tree(tree, f):
    # conditional iteration over all child nodes / leaves
    return tuple(elem for elem in tree if type(elem) != tuple and f(elem)) \ 
         + tuple(filter_tree(node, f) for node in tree if type(node) == tuple)

>>> filter_tree((1,2,3,4,(1,2,3)),lambda x:x%2==0)
(2, 4, (2,))

【讨论】:

  • 看不懂什么是\+?
  • @AlexK 只是为了整洁——\ 允许您在新行上继续声明(请参阅here);当然,这完全是可选的
  • @AlexK,我不相信这个解决方案有效。给定输入(0, 1, 2, (3, 4, (5, 6), 7), 8, 9, 10),它返回(0, 2, 8, 10, (4, (6,))) 而不是(0, 2, (4, (6,)), 8, 10)。 IE。它不保留输入的递归结构,它只是递归地收集偶数。
  • @cdlane 不错;我已经上传了一个满足这一点的版本(尽管 OP 没有明确说明需要保留顺序);但是原始版本确实保留了基本的递归结构。
【解决方案3】:

我想这就是你想要的。

a = (1,2,3,(1,2,3,(1,2,3,4)))

def func(x):
    return x%2==0

def foo(v):
    ans = ()
    for x in v:
        if type(x)==tuple:
            ans += (foo(x),)
        elif func(x):
            ans += (x,)
    return ans

print(foo(a))

【讨论】:

  • 这不是递归的。
  • @AlexK ans += (foo(x),)?
  • @AlexK 它的递归 ans += (foo(x),) 将对每个元组类型进行递归调用并将其添加到答案中。并且 (foo(x),) 是在您的最终答案中保留元组结构。
  • 这是一个不错的方法,但你没有将过滤器函数作为一个参数,我认为这会增加更多的复杂性。
【解决方案4】:

保留输入的递归结构的替代单行解决方案:

def filter_tree(tree, filter_func):
    return tuple(filter_tree(element, filter_func) if isinstance(element, tuple) else element for element in tree if isinstance(element, tuple) or filter_func(element))

测试

>>> print(filter_tree((0, 1, 2, (3, 4, (5, 6), 7), 8, 9, 10), lambda x: x % 2 == 0))
(0, 2, (4, (6,)), 8, 10)
>>> 

大致相当于:

def filter_tree(tree, filter_func):

    filtered_tree = tuple()

    for element in tree:

        if isinstance(element, tuple):
            filtered_tree += (filter_tree(element, filter_func),)
        elif filter_func(element):
            filtered_tree += (element,)

    return filtered_tree

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-12-03
    • 2020-12-29
    • 2016-09-11
    • 1970-01-01
    • 1970-01-01
    • 2017-10-08
    • 2013-12-05
    相关资源
    最近更新 更多