Coroutine vs Tiếp tục vs Máy phát điện


147

Sự khác biệt giữa coroutine và tiếp tục và máy phát điện là gì?


2
Tôi tự hỏi nếu coroutines và continuations là tương đương có hiệu quả. Tôi biết có thể mô hình hóa các xác chết với các phần tiếp theo, nhưng liệu có thể mô hình hóa các phần tiếp theo với các phần tử hay không bởi vì các phần tiếp theo mạnh hơn?
kể từ

Câu trả lời:


127

Tôi sẽ bắt đầu với máy phát điện, xem chúng là trường hợp đơn giản nhất. Như @zvolkov đã đề cập, chúng là các hàm / đối tượng có thể được gọi liên tục mà không trả về, nhưng khi được gọi sẽ trả về (nhường) một giá trị và sau đó tạm dừng thực thi. Khi họ được gọi lại, họ sẽ bắt đầu từ nơi họ bị đình chỉ hành quyết lần cuối và làm lại việc của họ.

Một máy phát điện về cơ bản là một coroutine (không đối xứng). Sự khác biệt giữa coroutine và máy phát điện là coroutine có thể chấp nhận các đối số sau khi được gọi ban đầu, trong khi máy phát điện thì không thể.

Có một chút khó khăn khi đưa ra một ví dụ tầm thường về nơi bạn sử dụng coroutines, nhưng đây là nỗ lực tốt nhất của tôi. Lấy mã Python này (tạo thành) làm ví dụ.

def my_coroutine_body(*args):
    while True:
        # Do some funky stuff
        *args = yield value_im_returning
        # Do some more funky stuff

my_coro = make_coroutine(my_coroutine_body)

x = 0
while True:
   # The coroutine does some funky stuff to x, and returns a new value.
   x = my_coro(x)
   print x

Một ví dụ về nơi coroutines được sử dụng là lexers và trình phân tích cú pháp. Không có coroutines trong ngôn ngữ hoặc được mô phỏng bằng cách nào đó, mã lexing và phân tích cú pháp cần phải được trộn lẫn với nhau mặc dù chúng thực sự là hai mối quan tâm riêng biệt. Nhưng bằng cách sử dụng coroutine, bạn có thể tách mã lexing và phân tích cú pháp.

(Tôi sẽ tìm hiểu sự khác biệt giữa các coroutine đối xứng và bất đối xứng. Chỉ cần nói rằng chúng tương đương nhau, bạn có thể chuyển đổi từ cái này sang cái khác, và coroutines không đối xứng - giống như máy phát điện - là dễ hiểu hơn. Tôi đã phác thảo cách người ta có thể triển khai các coroutines không đối xứng trong Python.)

Tiếp tục là những con thú khá đơn giản. Tất cả chúng là, là các hàm đại diện cho một điểm khác trong chương trình, nếu bạn gọi nó, sẽ khiến việc thực thi tự động chuyển sang điểm mà hàm đại diện. Bạn sử dụng các phiên bản rất hạn chế của chúng mỗi ngày mà không nhận ra. Các ngoại lệ, ví dụ, có thể được coi là một loại tiếp tục từ trong ra ngoài. Tôi sẽ cung cấp cho bạn một ví dụ mã giả dựa trên Python về sự tiếp nối.

Giả sử Python có một hàm được gọi callcc()và hàm này có hai đối số, đầu tiên là hàm và thứ hai là danh sách các đối số để gọi nó với. Hạn chế duy nhất đối với chức năng đó là đối số cuối cùng cần có sẽ là một chức năng (sẽ là sự tiếp tục hiện tại của chúng tôi).

def foo(x, y, cc):
   cc(max(x, y))

biggest = callcc(foo, [23, 42])
print biggest

Điều gì sẽ xảy ra là callcc()lần lượt gọi foo()với phần tiếp theo hiện tại ( cc), nghĩa là một tham chiếu đến điểm trong chương trình callcc()được gọi. Khi foo()gọi tiếp tục hiện tại, về cơ bản giống như yêu callcc()cầu trả về với giá trị mà bạn gọi là tiếp tục hiện tại và khi thực hiện điều đó, nó sẽ quay trở lại ngăn xếp đến nơi tiếp tục hiện tại được tạo, tức là khi bạn gọi callcc().

Kết quả của tất cả những điều này sẽ là biến thể Python giả định của chúng tôi sẽ in '42'.

Tôi hy vọng điều đó có ích, và tôi chắc rằng lời giải thích của tôi có thể được cải thiện khá nhiều!


6
Một nit: phân continuations là chức năng, nhưng undelimited continuations không: okmij.org/ftp/continuations/undelimited.html#delim-vs-undelim
Frank Shearar

2
Đó là một điểm hay. Điều đó nói rằng, trong hầu hết các ứng dụng thực tế, khi mọi người nói 'tiếp tục', họ đang nói về các phần tiếp theo một phần / phân định. Đưa vào các loại tiếp tục khác nhau sẽ làm vẩn đục phần nào giải thích.
Keith Gaughan

1
Tiếp tục không phải là chức năng, mặc dù chúng có thể được thống nhất thành chức năng. "Điều đó nói rằng, trong hầu hết các ứng dụng thực tế, khi mọi người nói 'tiếp tục', họ đang nói về các phần tiếp theo một phần / phân định." Bạn có muốn chỉ việc sử dụng thuật ngữ "tiếp tục" như vậy không? Tôi chưa bao giờ gặp việc sử dụng như vậy. Ngoài ra, bạn đã đưa ra một ví dụ cho việc tiếp tục không giới hạn, bằng cách sử dụng cuộc gọi / cc. Các toán tử cho các phần tiếp theo được phân tách thường là "đặt lại" và "thay đổi" (chúng có thể có các tên khác).
Ivancho

3
Hãy bắt đầu với thực tế là đã năm năm kể từ khi tôi viết bài này. Bạn đến bữa tiệc muộn một chút. Thứ hai, tôi biết rằng các phần tiếp theo không bị giới hạn không có chức năng, nhưng bạn về việc bạn cố gắng giải thích cách chúng hoạt động mà không đề cập đến chúng như vậy trong khi vẫn giữ ngôn ngữ đơn giản. Theo quan điểm của lập trình viên trung bình, thực tế là việc tiếp tục không bị giới hạn không trở lại chỉ làm cho nó trở thành hàm một lần, không đúng theo định nghĩa của hàm là gì, nhưng ít nhất có thể hiểu được .
Keith Gaughan

2
Tôi không đến dự tiệc vì đây là kết quả đầu tiên tôi nhận được trên google khi tôi tìm kiếm "coroutine vs Generator". Tôi đã hy vọng tìm thấy một số thông tin tốt về sự khác biệt của họ. Dù sao tôi đã tìm thấy nó ở nơi khác. Và tôi không phải là người đầu tiên chỉ ra rằng lời giải thích của bạn về việc tiếp tục là sai. Vấn đề là ai đó sẽ hiểu sai và có thể bị nhầm lẫn sau đó khi cô ấy hoặc anh ấy gặp cùng một từ được sử dụng cho một cái gì đó khác nhau.
Ivancho

33

Coroutine là một trong một số thủ tục thay phiên nhau thực hiện công việc của họ và sau đó tạm dừng để kiểm soát các coroutine khác trong nhóm.

Tiếp tục là một "con trỏ tới một hàm" mà bạn chuyển đến một số thủ tục, để được thực thi ("tiếp tục với") khi thủ tục đó được thực hiện.

Trình tạo (trong .NET) là một cấu trúc ngôn ngữ có thể phun ra một giá trị, "tạm dừng" thực thi phương thức và sau đó tiến hành từ cùng một điểm khi được yêu cầu giá trị tiếp theo.


Tôi nhận ra câu trả lời có thể không chính xác nhưng ở cấp độ câu hỏi này tôi đã cố gắng giữ nó đơn giản. Bên cạnh đó, tôi không thực sự hiểu tất cả những điều này :)
zvolkov

Một trình tạo trong python tương tự như phiên bản C #, nhưng được triển khai như một cú pháp đặc biệt để tạo một thể hiện của đối tượng iterator, trả về các giá trị được trả về bởi định nghĩa "hàm" mà bạn cung cấp.
Benson

2
Một chỉnh sửa nhỏ: "... bao gồm ngăn xếp cuộc gọi và tất cả các biến NHƯNG KHÔNG PHẢI GIÁ TRỊ CỦA CHÚNG" (hoặc chỉ bỏ "tất cả các biến"). Tiếp tục không bảo tồn các giá trị, chúng chỉ chứa ngăn xếp cuộc gọi.
kể từ

Không, phần tiếp theo không phải là "con trỏ tới một hàm". Trong triển khai ngây thơ nhất, nó chứa một con trỏ tới hàm và một môi trường chứa các biến cục bộ. Và nó không bao giờ trả lại trừ khi bạn sử dụng một cái gì đó như cuộc gọi / cc để ghi lại nó với giá trị trả về.
NalaGinrut

9

Trong phiên bản mới hơn của Python, bạn có thể gửi các giá trị cho Trình tạo generator.send(), điều này làm cho Trình tạo python phát triển hiệu quả.

Sự khác biệt chính giữa python Generator và trình tạo khác, như greenlet, là trong python, bạn yield valuechỉ có thể quay lại với người gọi. Khi ở trong greenlet, target.switch(value)có thể đưa bạn đến một coroutine mục tiêu cụ thể và mang lại một giá trị nơi mà targetnó sẽ tiếp tục chạy.


3
Nhưng trong Python, tất cả các yieldcuộc gọi phải ở cùng một chức năng, được gọi là "Trình tạo". Bạn không thể yieldtừ một hàm phụ, đó là lý do tại sao Python được gọi là semi-coroutines , trong khi Lua có các coroutines không đối xứng . (Có những đề xuất để tuyên truyền sản lượng, nhưng tôi nghĩ rằng những điều đó chỉ làm
vẩn

7
@ cdunn2001: (nhận xét của Winston) Python3.3 đã giới thiệu biểu thức "suất từ" cho phép bạn sinh ra từ trình tạo phụ.
Linus Caldwell
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.