Verim kullanarak özyineleme


84

Özyineleme ile yieldifadeyi karıştırmanın bir yolu var mı ? Örneğin, sonsuz sayı üreteci (özyineleme kullanarak) şöyle bir şey olabilir:

def infinity(start):
    yield start
    # recursion here ...

>>> it = infinity(1)
>>> next(it)
1
>>> next(it)
2

Denedim:

def infinity(start):
    yield start
    infinity(start + 1)

ve

def infinity(start):
    yield start
    yield infinity(start + 1)

Ama hiçbiri istediğimi yapmadı, ilki geldikten sonra durdu startve ikincisi geri döndü start, sonra jeneratör ve sonra durdu.

NOT: Lütfen bunu bir süre döngüsü kullanarak yapabileceğinizi biliyorum:

def infinity(start):
    while True:
        yield start
        start += 1

Bunun yinelemeli yapılıp yapılamayacağını bilmek istiyorum.


Bir süre önce sorduğum bu soruya iyi bir yanıt için [buraya] [1] bakın. [1]: stackoverflow.com/questions/5704220/…
sizzzzlerz

Not: Bunu yapmanın doğru yolu itertools.count, kendi çözümünüzü, döngü tabanlı veya başka bir yöntemi kullanmak yerine kullanmak olacaktır .
Petr Viktorin

8
@PetrViktorin bu sadece bir örnek, sonsuz sayılar üretmek hiç de gerçek sorun değil
juliomalegria

Yanıtlar:


157

Evet, bunu yapabilirsin:

def infinity(start):
    yield start
    for x in infinity(start + 1):
        yield x

Ancak maksimum özyineleme derinliğine ulaşıldığında bu hata verecektir.

Python 3.3'ten başlayarak, kullanabileceksiniz

def infinity(start):
    yield start
    yield from infinity(start + 1)

yield fromOluşturucu işlevinizi, döngü yapmadan ya da döngü yapmadan yinelemeli olarak çağırırsanız , tek yapmanız gereken işlev gövdesini çalıştırmadan ya da herhangi bir şey üretmeden yeni bir jeneratör oluşturmaktır.

Daha fazla ayrıntı için PEP 380'e bakınız.


13
Ama öyle görünüyor ki yield fromhala bir özyineleme limiti var :(
Jo So

3
her zaman bir özyineleme limiti olacaktır
Radyo Kontrollü

12

Bazı durumlarda, üreteçler için özyineleme yerine bir yığın kullanmak tercih edilebilir. Bir yığın ve while döngüsü kullanarak özyinelemeli bir yöntemi yeniden yazmak mümkün olmalıdır.

Aşağıda, geri arama kullanan ve yığın mantığı kullanılarak yeniden yazılabilen özyinelemeli bir yöntem örneği verilmiştir:

def traverse_tree(callback):
    # Get the root node from somewhere.
    root = get_root_node()
    def recurse(node):
        callback(node)
        for child in node.get('children', []):
            recurse(child)
    recurse(root)

Yukarıdaki yöntem, her düğümün childrençocuk düğümler içerebilen bir diziye sahip olduğu bir düğüm ağacını geçer . Her bir düğümle karşılaşıldığında, geri arama yapılır ve geçerli düğüm ona iletilir.

Yöntem bu şekilde, her bir düğümde bazı özellikler yazdırarak kullanılabilir.

def callback(node):
    print(node['id'])
traverse_tree(callback)

Bunun yerine bir yığın kullanın ve geçiş yöntemini bir jeneratör olarak yazın

# A stack-based alternative to the traverse_tree method above.
def iternodes():
    stack = [get_root_node()]
    while stack:
        node = stack.pop()
        yield node
        for child in reversed(node.get('children', [])):
            stack.append(child)

(Orijinalle aynı geçiş sırasını istiyorsanız, alt sırayı tersine çevirmeniz gerektiğini unutmayın çünkü yığına eklenen ilk alt öğe atılan son alt öğe olacaktır.)

Şimdi traverse_treeyukarıdaki ile aynı davranışı elde edebilirsiniz , ancak bir jeneratör ile:

for node in iternodes():
    print(node['id'])

Bu, her şeye uyan tek bir çözüm değildir, ancak bazı üreticiler için özyineleme yerine yığın işlemeyi değiştiren güzel bir sonuç elde edebilirsiniz.


3
Güzel cevap! Python 2.7'de verim özyineleme ile gerçekten kullanılamaz, ancak yığını elle yöneterek aynı etkiyi elde edebilirsiniz.
00prometheus

0
def lprint(a):
    if isinstance(a, list):
        for i in a:
            yield from lprint(i)
    else:
        yield a

b = [[1, [2, 3], 4], [5, 6, [7, 8, [9]]]]
for i in lprint(b):
    print(i)

Nedir b? Yalnızca kod yanıtlarını bırakmamaya çalışın ... Biraz açıklama ve açıklama, olayları bağlama
oturtmanıza

i in lprint (a) için: print (i)
Юрий Блинков

Cevabı neden daha net olacak şekilde düzenlemiyorsunuz? Seni küçük tıklayarak yapabilirsiniz editCevabınız aşağıdaki etiketi veya tıklama burada . Ayrıca, dediğim gibi, bunun sorunu nasıl ve neden çözdüğüne dair küçük bir açıklama eklemeye çalışın
Tomerikoo

-3

Yani temelde , işlevinizi özyinelemeli olarak çağırmanız gereken yere bir for döngüsü eklemeniz yeterlidir . Bu Python 2.7 için geçerlidir.


1
bu cevap daha fazla ayrıntı gerektirse de, aslında Sven Marnach'ın kabul ettiği cevaba uygun, ilk kod parçasına bakın ...
Coffee_Table
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.