Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/345.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何确定一个容器是否是无限递归的,并找到其最小的唯一容器?_Python_Python 3.x_Recursion - Fatal编程技术网

Python 如何确定一个容器是否是无限递归的,并找到其最小的唯一容器?

Python 如何确定一个容器是否是无限递归的,并找到其最小的唯一容器?,python,python-3.x,recursion,Python,Python 3.x,Recursion,我正在阅读,并决定将其作为一个Python练习来采用——这是一个小函数,我偶尔会重写它,而不参考原始函数,只是为了练习。我第一次尝试这种方法时,我得到了如下结果: def flat(iterable): try: iter(iterable) except TypeError: yield iterable else: for item in iterable: yield from flatten(

我正在阅读,并决定将其作为一个Python练习来采用——这是一个小函数,我偶尔会重写它,而不参考原始函数,只是为了练习。我第一次尝试这种方法时,我得到了如下结果:

def flat(iterable):
    try:
        iter(iterable)
    except TypeError:
        yield iterable
    else:
        for item in iterable:
            yield from flatten(item)
def flatter(iterable):
    try:
        iter(iterable)
        if isinstance(iterable, str):
            raise TypeError
    except TypeError:
        yield iterable
    else:
        for item in iterable:
            yield from flatten(item)
这对于包含数字的嵌套
list
s这样的基本结构很好,但字符串会使其崩溃,因为字符串的第一个元素是单个字符串,其中第一个元素是字符串本身,而第一个元素又是字符串本身,依此类推。检查上面链接的问题,我意识到这解释了字符串的检查。这给了我以下几点:

def flat(iterable):
    try:
        iter(iterable)
    except TypeError:
        yield iterable
    else:
        for item in iterable:
            yield from flatten(item)
def flatter(iterable):
    try:
        iter(iterable)
        if isinstance(iterable, str):
            raise TypeError
    except TypeError:
        yield iterable
    else:
        for item in iterable:
            yield from flatten(item)
现在它也适用于字符串。然而,我随后回忆起,
列表可以包含对自身的引用

>>> lst = []
>>> lst.append(lst)
>>> lst
[[...]]
>>> lst[0][0][0][0] is lst
True
因此,字符串不是唯一可能导致此类问题的类型。在这一点上,我开始寻找一种不用显式类型检查就可以防止这个问题的方法

随后出现了以下
flatter.py
flattish()
是一个只检查字符串的版本
flatte_notype()
检查对象的第一个项的第一个项是否等于自身,以确定递归<代码>展平()
执行此操作,然后检查对象或其第一项的第一项是否为另一项类型的实例。
Fake
类基本上只是为序列定义了一个包装器。测试每个函数的行上的注释以
的形式描述结果,应该是“期望的结果”[>“不期望的实际结果”]
。如您所见,每种方法都会以不同的方式失败,例如,将
包装在字符串上,将
包装在整数、单个字符串和多个字符串的
列表上

def flattish(*i):
    for item in i:
        try: iter(item)
        except: yield item
        else:
            if isinstance(item, str): yield item
            else: yield from flattish(*item)

class Fake:
    def __init__(self, l):
        self.l = l
        self.index = 0
    def __iter__(self):
        return self
    def __next__(self):
        if self.index >= len(self.l):
            raise StopIteration
        else:
            self.index +=1
            return self.l[self.index-1]
    def __str__(self):
        return str(self.l)

def flatten_notype(*i):
    for item in i:
        try:
            n = next(iter(item))
            try:
                n2 = next(iter(n))
                recur = n == n2
            except TypeError:
                yield from flatten(*item)
            else:
                if recur:
                    yield item
                else:
                    yield from flatten(*item)
        except TypeError:
            yield item

def flatten(*i):
    for item in i:
        try:
            n = next(iter(item))
            try:
                n2 = next(iter(n))
                recur = n == n2
            except TypeError:
                yield from flatten(*item)
            else:
                if recur:
                    yield item if isinstance(n2, type(item)) or isinstance(item, type(n2)) else n2
                else:
                    yield from flatten(*item)
        except TypeError:
            yield item


f = Fake('abc')

print(*flattish(f)) # should be `abc`
print(*flattish((f,))) # should be `abc` > ``
print(*flattish(1, ('a',), ('bc',))) # should be `1 a bc`

f = Fake([1, 2, 3])

print(*flattish(f)) # should be `1 2 3`
print(*flattish((f,))) # should be `1 2 3` > ``
print(*flattish(1, ('a',), ('bc',))) # should be `1 a bc`

f = Fake('abc')
print(*flatten_notype(f)) # should be `abc`
print(*flatten_notype((f,))) # should be `abc` > `c`
print(*flatten_notype(1, ('a',), ('bc',))) # should be `1 a bc` > `1 ('a',) bc`

f = Fake([1, 2, 3])     

print(*flatten_notype(f)) # should be `1 2 3` > `2 3`
print(*flatten_notype((f,))) # should be `1 2 3` > ``
print(*flatten_notype(1, ('a',), ('bc',))) # should be `1 a bc` > `1 ('a',) bc`

f = Fake('abc')
print(*flatten(f)) # should be `abc` > `a`
print(*flatten((f,))) # should be `abc` > `c`
print(*flatten(1, ('a',), ('bc',))) # should be `1 a bc`

f = Fake([1, 2, 3])     

print(*flatten(f)) # should be `1 2 3` > `2 3`
print(*flatten((f,))) # should be `1 2 3` > ``
print(*flatten(1, ('a',), ('bc',))) # should be `1 a bc`
我还使用上面定义的递归
lst
flant()
尝试了以下操作:

正如您所看到的,它的失败与
1('a',)bc类似,也有其特殊的方式

我读到这篇文章时认为,也许函数可以跟踪它所看到的每个对象,但这也行不通,因为我们的
lst
包含一个具有匹配标识和相等的对象,字符串包含可能只有匹配相等的对象,而相等是不够的,因为有可能出现类似
展平的情况([1,2],[1,2])


是否有可靠的方法(即不只是检查已知的类型,不要求递归容器及其容器都是相同的类型,等等)检查一个容器是否包含具有潜在无限递归的iterable对象,并可靠地确定最小的唯一容器?如果有,请说明如何实现,为什么可靠,以及它如何处理各种递归情况。如果没有,请说明为什么这在逻辑上是不可能的。

我不认为有问题确定任意iterable是否为无穷大的可靠方法。我们能做的最好的方法是从这样一个iterable中无限地产生原语,而不耗尽堆栈,例如:

from collections import deque

def flat(iterable):

    d = deque([iterable])

    def _primitive(x):
        return type(x) in (int, float, bool, str, unicode)

    def _next():
        x = d.popleft()
        if _primitive(x):
            return True, x
        d.extend(x)
        return False, None

    while d:
        ok, x = _next()
        if ok:
            yield x


xs = [1,[2], 'abc']
xs.insert(0, xs)

for p in flat(xs):
    print p

“原始”的上述定义是原始的,但这肯定可以改进。

这样的定义如何:

def flat(obj, used=[], old=None):        
    #This is to get inf. recurrences
    if obj==old:
        if obj not in used:
            used.append(obj)
            yield obj
        raise StopIteration
    try:
        #Get strings
        if isinstance(obj, str):
            raise TypeError
        #Try to iterate the obj
        for item in obj:
            yield from flat(item, used, obj)
    except TypeError:
        #Get non-iterable items
        if obj not in used:
            used.append(obj)
            yield obj
经过有限数量的(递归)步骤后,列表最多包含一个iterable元素(因为我们必须在有限的多个步骤中生成它)。这就是我们用
obj==old
测试的内容,其中
obj
old
元素中

列表
used
跟踪所有元素,因为我们只需要每个元素一次。我们可以删除它,但我们会得到一个丑陋的(更重要的是定义不明确的)行为,元素的频率是多少。 缺点是我们将整个列表存储在使用的列表的末尾

用一些列表测试这一点似乎有效:

>> lst = [1]
>> lst.append(lst)
>> print('\nList1:   ', lst)       
>> print([x for x in flat(lst)])
List1:     [1, [...]]
Elements: [1, [1, [...]]]

#We'd need to reset the iterator here!
>> lst2 = []
>> lst2.append(lst2)
>> lst2.append((1,'ab'))
>> lst2.append(lst)
>> lst2.append(3)
>> print('\nList2:   ', lst2)       
>> print([x for x in flat(lst2)])
List2:     [[...], (1, 'ab'), [1, [...]], 3]
Elements: [[[...], (1, 'ab'), [1, [...]], 3], 1, 'ab', [1, [...]], 3]

注意:无限列表
[…],(1,'ab'),[1,[…],[3]
[1,[…]
被视为元素,因为这些元素实际上包含它们自己,但如果不希望这样做,可以注释掉上面代码中的第一个
yield

测试代码中存在一个与您试图解决的递归容器问题无关的问题。问题是您的
类是一个迭代器nd只能使用一次。在您对其所有值进行迭代后,当您再次尝试对其进行迭代时,它将始终引发
StopIteration

因此,如果您在同一个
false
实例上执行多个操作,那么在第一个操作消耗了迭代器之后,您不应该期望得到任何空输出。如果您在每个操作之前重新创建迭代器,您就不会有这个问题(实际上您可以尝试解决递归问题)

关于这个问题。避免无限递归的一种方法是维护一个包含当前嵌套对象的堆栈。如果您看到的下一个值已经在堆栈的某个位置上,您知道它是递归的,可以跳过它。下面是使用列表作为堆栈的实现:

def flatten(obj, stack=None):
    if stack is None:
        stack = []

    if obj in stack:
        yield obj

    try:
        it = iter(obj)
    except TypeError:
        yield obj
    else:
        stack.append(obj)
        for item in it:
            yield from flatten(item, stack)
        stack.pop()
请注意,这仍然可以多次从同一容器中生成值,只要它不嵌套在自身中(例如,对于
x=[1,2];y=[x,3,x];print(*flatte(y))
将打印
12312


它也会递归到字符串中,但它只会在一个级别执行,所以
flatten(“foo”)
将依次产生字母
'f'
'o'
'o'
。如果您想避免这种情况,您可能需要函数具有类型意识,因为从迭代协议的角度来看,字符串与其字母的可编辑容器没有任何区别。它只是一个递归的单个字符串包含它们自己。

您所问的场景定义非常松散。正如您问题中所定义的,逻辑上不可能“检查”
>>> list(flatten(l, keepcls=(dict, str)))
[1, 2, 5, 6, {'a': 1, 'b': 2}, 7, 'string', [1, 2, [5, 6, {'a': 1, 'b': 2}, 7, 'string'], [...]]]
>>> list(flatten([[1,2],[1,[1,2]],[1,2]]))
[1, 2, 1, 1, 2, 1, 2]