Đệ quy sử dụng lợi suất


82

Có cách nào để trộn đệ quy và yieldcâu lệnh không? Ví dụ, một trình tạo số vô hạn (sử dụng đệ quy) sẽ giống như sau:

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

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

Tôi đã thử:

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

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

Nhưng không ai trong số họ làm theo ý tôi, cái đầu tiên dừng lại sau khi nó mang lại hiệu quả startvà cái thứ hai cho kết quả start, sau đó là máy phát điện và sau đó dừng lại.

LƯU Ý: Xin vui lòng, tôi biết bạn có thể làm điều này bằng cách sử dụng vòng lặp trong khi:

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

Tôi chỉ muốn biết nếu điều này có thể được thực hiện một cách đệ quy.


Xem [tại đây] [1] để có câu trả lời hay cho câu hỏi mà tôi đã đặt ra một thời gian. [1]: stackoverflow.com/questions/5704220/…
sizzzzlerz

Lưu ý: cách thích hợp để làm điều này là sử dụng itertools.countthay vì sử dụng giải pháp của riêng bạn, dựa trên vòng lặp hoặc phương pháp khác.
Petr Viktorin

8
@PetrViktorin này chỉ là một ví dụ, tạo ra vô hạn số không phải là ở tất cả các vấn đề thực sự
juliomalegria

Câu trả lời:


154

Có, bạn có thể làm điều này:

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

Tuy nhiên, điều này sẽ xảy ra khi đạt đến độ sâu đệ quy tối đa.

Bắt đầu từ Python 3.3, bạn sẽ có thể sử dụng

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

Nếu bạn chỉ gọi hàm trình tạo của mình một cách đệ quy mà không lặp lại nó hoặc yield from-ing nó, tất cả những gì bạn làm là xây dựng một trình tạo mới mà không thực sự chạy thân hàm hoặc mang lại bất kỳ thứ gì.

Xem PEP 380 để biết thêm chi tiết.


12
Nhưng có vẻ như yield fromvẫn có giới hạn đệ quy :(
Jo So

2
sẽ luôn có giới hạn đệ quy
Radio Control

12

Trong một số trường hợp, có thể thích sử dụng ngăn xếp thay vì đệ quy cho trình tạo. Có thể viết lại một phương thức đệ quy bằng cách sử dụng ngăn xếp và vòng lặp while.

Dưới đây là một ví dụ về phương thức đệ quy sử dụng một lệnh gọi lại và có thể được viết lại bằng cách sử dụng logic ngăn xếp:

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)

Phương thức trên duyệt qua một cây nút trong đó mỗi nút có một childrenmảng có thể chứa các nút con. Khi gặp mỗi nút, lệnh gọi lại được đưa ra và nút hiện tại được chuyển cho nó.

Phương thức có thể được sử dụng theo cách này, in ra một số thuộc tính trên mỗi nút.

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

Thay vào đó, hãy sử dụng ngăn xếp và viết phương thức truyền tải dưới dạng trình tạo

# 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)

(Lưu ý rằng nếu bạn muốn cùng một thứ tự truyền tải như ban đầu, bạn cần đảo ngược thứ tự các con vì con đầu tiên được nối vào ngăn xếp sẽ là con cuối cùng xuất hiện.)

Bây giờ bạn có thể nhận được hành vi tương tự như traverse_treetrên, nhưng với trình tạo:

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

Đây không phải là một giải pháp phù hợp với tất cả nhưng đối với một số trình tạo, bạn có thể nhận được kết quả tốt đẹp thay thế xử lý ngăn xếp cho đệ quy.


3
Câu trả lời rất hay! Lợi nhuận trong python 2.7 không thực sự có thể được sử dụng với đệ quy, nhưng bằng cách quản lý ngăn xếp theo cách thủ công, bạn có thể nhận được hiệu quả tương tự.
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)

bgì? Cố gắng không rời câu trả lời mã chỉ ... Một chút làm rõ và giải thích sẽ giúp đặt mọi thứ trong bối cảnh và hiểu rõ hơn về câu trả lời của bạn
Tomerikoo

cho tôi trong lprint (a): print (i)
Юрий Блинков

Tại sao không chỉnh sửa câu trả lời để nó rõ ràng hơn? Bạn có thể làm điều đó bằng cách nhấp vào editthẻ nhỏ bên dưới câu trả lời của bạn hoặc nhấp vào đây . Ngoài ra, như tôi đã nói cố gắng thêm một chút giải thích cho như thế nào và tại sao điều này giải quyết vấn đề
Tomerikoo

-3

Vì vậy, về cơ bản bạn chỉ cần thêm một vòng lặp for tại nơi bạn cần gọi hàm của mình một cách đệ quy . Điều này áp dụng cho Python 2.7.


1
mặc dù câu trả lời này yêu cầu chi tiết hơn, nhưng nó thực sự phù hợp với câu trả lời được chấp nhận của Sven Marnach, hãy xem đoạn mã đầu tiên của anh ấy ...
Coffee_Table
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.