测试某个值是否不是由迭代器产生的

Testing if some value is not produced by an iterator

我想测试某个值是否属于使用迭代器生成的数字序列。当然当值属于序列时,一满足就可以停止。但是当它 没有 时,我想这就是问题出现的时候。

但是可以使用附加信息(例如,序列在增加)。

考虑斐波那契示例:

class FibonacciIterator(object):
    def __init__(self):
        self.mem = [0, 1]
    def __next__(self):
        curr = self.mem[0]
        new = self.mem[0]+self.mem[1]
        self.mem[0] = self.mem[1]
        self.mem[1] = new
        return curr

class Fibonacci(object):
    def __iter__(self):
       return FibonacciIterator()

如果测试 8 是否属于序列那么一切都很好:

>>> fib = Fibonacci()
>>> 8 in fib
True

但是,如果一个测试 10(不属于序列)则

>>> 10 in fib
...

永不终止。但是通过观察 8 之后出现 13,可以很容易地确定 10 不在序列中,并且由于序列在增加,因此必然 not 10 in fib.

在 Python 中是否有一个很好的方法来实现 in 的这种行为,以便 10 in fib 终止?

简单的解决方案是实现一个 __contains__ 方法,该方法只迭代迭代器的副本:

class FibonacciIterator(object):
    def __init__(self):
        self.mem = [0, 1]

    def __iter__(self):
        return self

    def __contains__(self, value):
        testit = FibonacciIterator()
        testit.mem = list(self.mem)  # copy
        for item in testit:
            if item == value:
                return True
            if item > value:
                return False

    def __next__(self):
        curr = self.mem[0]
        self.mem = self.mem[1], sum(self.mem)
        return curr

class Fibonacci(object):
    def __iter__(self):
        return FibonacciIterator()

    def __contains__(self, value):
        return value in iter(self)

然而,还有另一种方法可以确定一个数是否为斐波那契数:如果 (5*n**2 + 4)(5*n**2 – 4) 或两者都是完全平方数(整数的平方数)。

我将使用 this answer 中的方法来实现 is_square 函数:

def is_square(apositiveint):
    # Source: 
    # Credit: Alex Martelli

    x = apositiveint // 2
    seen = {x}
    while x * x != apositiveint:
        x = (x + (apositiveint // x)) // 2
        if x in seen: 
            return False
        seen.add(x)
    return True

class FibonacciIterator(object):
    def __init__(self):
        self.mem = [0, 1]

    def __iter__(self):
        return self

    def __contains__(self, value):
        if value < self.mem[0]:
            return False
        else:
            # Just check if at least one of both is a perfect square
            value_squared_times_five = 5*value**2
            return is_square(value_squared_times_five + 4) or is_square(value_squared_times_five - 4)

    def __next__(self):
        curr = self.mem[0]
        self.mem = self.mem[1], sum(self.mem)
        return curr

class Fibonacci(object):
    def __iter__(self):
        return FibonacciIterator()

    def __contains__(self, value):
        return value in iter(self)