13 марта 2009

Генераторы Python и ленивые вычисления

Генераторы в Python позволяют работать с данными на том же уровне абстракции, что и ленивые языки. Например, бесконечный список, подобный [1..] в Clean, можно реализовать так:

def infinityList():
i = 1
while True:
yield i
i += 1

Работать с ним можно с помощью ленивых аналогов map и filter:
def lazyFilter(func, list):
for x in list:
if func(x):
yield x

def lazyMap(func, list):
for x in list:
yield func(x)
Например, чтобы найти 10 первых простых чисел (см. аналогичный пример для Clean), можно воспользоваться тестом на простоту:
def prime(x):
for i in range(x)[2:]:
if x % i == 0:
return False
return True
Тогда программа будет выглядеть как фильтрация бесконечного списка до получения искомого количества чисел:
primes = 0
for x in lazyFilter(prime, infinityList()):
print x
primes += 1
if primes == 10: break
Здесь бесконечный список как никогда уместен, т.к. нет аналитического способа узнать каким по счету будет N-е простое число. Правда, функциональный вариант все равно симпатичнее.

UPD. В Python 3000 функции map() и filter() будут возвращать итераторы, т.е. по определению будут ленивыми.

Комментариев нет: