生成无限列表?

Generating an infinite list?

知道这很可能会以失败告终,我想知道 Python 将如何处理这种情况以及我的代码是否实际上 "makes sense",至少在原则上是这样。 所以这是我的尝试,我对 python(几天前)还很陌生,所以我相信我的代码对那些有更多经验的人来说似乎很有趣,但请多多包涵。

def make_inf_lst(lst):
  lst = [0]
  while 1 == 1:
    for i in range(0, len(lst)):
      add = lst[i] + 1
      lst.append(add)
print(make_inf_lst([]))

这是我在 DOS window 中尝试 运行 以上内容时遇到的错误:

Traceback (most recent call last):
  File "break_python.py", line 7, in <module>
    print(make_inf_lst([]))
  File "break_python.py", line 6, in make_inf_lst
    lst.append(add)
MemoryError

提前致谢。

具有 "infinite" 数据结构的语言实际上正在生成延迟生成其值的对象,或 "on-demand"。他们不是预先创建整个列表,而是创建一个对象,当被要求提供一个值时,可以在那个时候创建​​它。

在 Python 中,使用生成器最容易管理。

def make_inf_sequence():
    x = 0
    while True:
        yield x
        x = x + 1

# This returns immediately
naturals = make_inf_sequence()

# This loop runs forever
for i in naturals:
    print(i)

无限数据结构确实有意义,但它们总是必须延迟计算。这意味着您的数据结构无法立即构建其所有元素,然后自行移动(就像我们通常对适合内存的 list 所做的那样)。相反,您的数据结构需要根据您的需要为您提供值。

在 Python 中,你不能有无限的 list 因为 list 是渴望的,而不是懒惰的(就像 Python 中的大多数事情一样,渴望是默认)。需要明确的是,我在这里谈论 list 类型 w.r.t。 Python 术语,而不是一些序列的抽象概念(某些人 可能 称之为列表)。

惰性求值的方法是使用一种叫做 generator 的东西。如果你想生成序列1,2,3,4,...,你可以用下面的代码来完成:

def gen_natural_numbers():
    cur = 1
    while True:
        yield cur
        cur += 1

natural_num_gen = gen_natural_numbers()
print("One: ", next(natural_num_gen))
print("Two: ", next(natural_num_gen))
print("Three: ", next(natural_num_gen))

这个输出:

One:  1
Two:  2
Three:  3

显然,您不必在 3 处停止。您不必在任何地方停止(除非您创建的 int 实际上不适合内存)。如果你一直调用 next,生成器会一直返回值给你。

随着您的进行,垃圾收集器可以从内存中删除不再包含引用的值。这完全是另一个话题,但应该注意的是,只要您不永远保留引用,您的程序在使用生成器时也不会 运行 内存不足。

基本上有两件事可以阻止打印无限列表(这似乎是您代码的最终目标):

  1. 计算机将 运行 内存不足。
  2. 列表的创建将花费无限时间,这意味着它永远不会完成,实际打印也永远不会开始。

如果你想象一台具有无限内存和无限计算速度的假想计算机,可以打印列表。

然而,某些语言通过 "lazy" 支持无限列表,这意味着它们将只计算列表中特定点所需的部分。然后你可以定义一个无限列表并要求它打印出来,例如其中的前 100 个值。

更多关于惰性计算的信息: https://en.m.wikipedia.org/wiki/Lazy_evaluation

从您可以立即访问宇宙中所有成员的意义上说,它不可能是无限的。那是不可能的——无限长度意味着需要无限的内存。从你得到的错误可以看出,你没有无限的内存。正如其他人所说,您可以用速度换取长度:

from copy import deepcopy
class infinite_ints:
    def __init__(self):
        self.list = []
        self.trans = lambda x: x;

    def __getitem__(self,at):
        try:
            return self.trans(self.list[at])
        except IndexError:
            self.list += [i for i in range(len(self.list),at+1)]
        return self.trans(self.list[at])

    def apply(self,function):
        new_list = deepcopy(self)
        new_list.trans = lambda x,f=function,p=self.trans: f(p(x))
        return new_list

>>> x[2]
2
>>> x[12]
12
>>> x[43]
43
>>> y = x.apply(lambda x: x+1)
>>> y[59]
60

在这里,如果需要,我将常规列表与即时扩展结合起来。我不必从一个空列表开始,如果我愿意的话,我可以预先制作前 N 个元素。

您当然可以重载运算符(+、- 随便什么)。关键是一切都是一次性完成的。以这种方式工作,您甚至可以添加和减去无限列表。

您甚至可以使用内存管理来改进这一点,如果有一段时间不查看,则删除列表的开头,但我认为这超出了范围。更实用的方法是保存当前数字和继续的方式(我添加了一个 prev 类似于内置 __next__:

def prev(x):
    return x.__prev__()

class infinity_functional:
    def __init__(self):
        self.n=0
    def __next__(self):
        Next   = infinity_functional()
        Next.n = self.n + 1
        return Next
    def __prev__(self):
        Prev   = infinity_functional()
        Prev.n = self.n - 1
        return Prev
    def __repr__(self):
        return str(self.n)

enter code here

>>>x=infinity_functional()
>>>x
0
>>> next(next(next(x)))
3
>>> y=next(next(next(x)))
>>> prev(y)
2

同样,可以轻松实现运算符(不仅仅是表示)或跳转多个运算符。

您的计算机在构建列表时有时内存不足,这就是导致您看到的错误的原因。无限列表是不可能的,因为内存是一种有限的资源。

或者,您可以使用无限迭代器。标准库提供了一些无限迭代器。根据你的例子,我认为最适合你的情况是 itertools.count.

示例用法:

>>> from itertools import count
>>> it = count()
>>> next(it)
0
>>> next(it)
1

您还可以指定自定义起点:

>>> from itertools import count
>>> it = count(5)
>>> next(it)
5
>>> next(it)
6

和自定义步骤:

>>> from itertools import count
>>> it = count(5, 2)
>>> next(it)
5
>>> next(it)
7
>>> next(it)
9