Một ví dụ về việc tiếp tục không được thực hiện như một thủ tục là gì?


15

Một cuộc thảo luận thú vị về sự khác biệt giữa các cuộc gọi lạitiếp tục về SO đã đặt ra câu hỏi này. Theo định nghĩa, tiếp tục là một đại diện trừu tượng của logic cần thiết để hoàn thành một tính toán. Trong hầu hết các ngôn ngữ, biểu thức này là một thủ tục một đối số mà bạn vượt qua bất kỳ giá trị nào cần tiếp tục xử lý.

Trong một ngôn ngữ chức năng thuần túy (trong đó tất cả các chức năng là công dân thuần túy và hạng nhất), tôi sẽ nghĩ rằng việc tiếp tục có thể được mô hình hóa hoàn toàn như một chức năng. Rốt cuộc, đây là cách mà trước đây tôi đã hiểu về sự tiếp nối cho đến thời điểm này. Tuy nhiên, thế giới đầy trạng thái (thở dài ..) và vì vậy định nghĩa chung không yêu cầu trạng thái chương trình bắt giữ liên tục - nó chỉ cần bao hàm ý định.

Để giúp tôi hiểu, một ví dụ có thể được cung cấp bằng ngôn ngữ chức năng trong đó phần tiếp theo được thể hiện theo cách trừu tượng hơn hàm không? Tôi biết Scheme cho phép bạn lấy tiếp tục hiện tại theo cách thức hạng nhất (cuộc gọi / cc), nhưng ngay cả như vậy, có vẻ như quy trình một đối số được truyền cho cuộc gọi / cc chỉ đơn giản được đưa ra tiếp tục hiện tại dưới dạng khác. thủ tục đối số mà hàm gọi / cc'd có thể áp dụng kết quả của nó.


Có lẽ giao điểm của các lục địa và phân nhánh như: các liên tục có thể được chuyển đổi thành các cấu trúc dữ liệu thông qua phân nhánh; có thể là một khu vực thú vị để xem xét.
Dan D.

@DanD. Bạn có gợi ý nào cho văn học thú vị mà tôi có thể đọc không? Chủ đề đó nghe có vẻ hữu ích.
David Cowden

Câu trả lời:


11

tl; dr; Các loại là trừu tượng bao quát trên một sự tiếp nối


Tiếp tục là loại đầu vào và đầu ra của nó

Điều gần nhất mà bạn sẽ tìm thấy đối với việc tiếp tục không dựa trên thủ tục có thể là đơn vị tiếp tục trong Haskell vì nó được biểu thị dưới dạng một loại, trong đó nhiều chức năng có thể được sử dụng để tương tác với loại để ngắt, tiếp tục, quay lui, et al.

Bạn có thể gói gọn việc đóng cửa đó trong một loại, chẳng hạn như Contloại trong Haskell nơi bạn có được sự trừu tượng hóa đơn nguyên như là một "sự trừu tượng hóa cấp độ cao hơn", và có những hình thức trừu tượng khác đối với sự tiếp tục mà bạn có được khi bạn xem tiếp tục như một loại thay vì đơn giản là một thủ tục , ví dụ

  • Bạn có thể thực hiện hai lần tiếp tục và thực hiện một thay thế giữa chúng nếu loại tuân theo luật là một hình đơn sắc
  • Bạn có thể trừu tượng về loại để thay đổi các loại đầu vào hoặc đầu ra của phần tiếp theo nếu bạn gói gọn việc đóng trong một loại tuân theo luật của functor
  • Bạn có thể tùy ý và áp dụng một phần hoặc trang trí phần tiếp theo của mình bằng chức năng như xác thực đầu vào hoặc chuyển đổi đầu vào nếu bạn đóng gói đóng trong một loại tuân theo luật của functor áp dụng

Đóng cửa so với thủ tục

Vào cuối ngày, về cơ bản bạn đúng; tiếp tục là một "thủ tục", mặc dù tôi muốn gọi nó là một đóng cửa. Thông thường các lần tiếp tục được thể hiện tốt nhất khi đóng cửa lớp đầu tiên đã bao quanh một môi trường ràng buộc. Trong một ngôn ngữ chức năng thuần túy, bạn có thể nói điều này không đặc biệt hợp lý vì bạn thiếu tài liệu tham khảo; Điều này đúng nhưng bạn có thể kèm theo các giá trị và phép gán đơn làm cho việc bao quanh giá trị so với tham chiếu giống hệt nhau. Điều này dẫn đến trong Haskell:

(\x -> \y -> insideYIcanAccess x (and y))

Một ngôn ngữ thiếu khả năng kèm theo một môi trường ràng buộc về mặt kỹ thuật có thể thiếu các lần đóng lớp đầu tiên, nhưng ngay cả khi đó có một số môi trường (nói chung là toàn cầu) có sẵn để đóng.

Vì vậy, tôi muốn nói rằng chính xác hơn để mô tả một phần tiếp theo là: Một bao đóng được sử dụng theo một cách cụ thể.


Phần kết luận

Đối với câu hỏi "Việc tiếp tục có thể thực hiện theo bất kỳ cách nào khác ngoài thủ tục không?" Không. Nếu bạn không có các hàm hạng nhất, bạn thực sự không thể có các phần tiếp theo như vậy (các con trỏ hàm có được tính là các hàm hạng nhất, do đó, việc truy cập bộ nhớ tùy ý có thể đủ).

Bây giờ với câu hỏi "Có cách nào để diễn đạt sự tiếp nối theo cách trừu tượng hơn thủ tục không?" Thể hiện nó như một kiểu mang lại cho bạn sự trừu tượng lớn hơn nhiều, cho phép bạn xử lý việc tiếp tục theo những cách rất chung chung để bạn có thể tương tác với việc tiếp tục theo nhiều cách hơn là chỉ thực hiện nó.


1
Điều này khái quát đến "Tiếp tục có thể đơn giản là bất cứ điều gì cho phép bạn nhận được kết quả của phần còn lại của chương trình". Vì điều này thường bắt buộc phải có một số mã (phần còn lại của chương trình) nên hầu hết các ngôn ngữ đều sử dụng các hàm. Về mặt lý thuyết bạn có thể xây dựng một sự tiếp nối từ bất cứ điều gì. Trong quá trình chuyển đổi tiếp tục trong trình biên dịch của tôi, tôi đã sử dụng các cây được lấp đầy một phần.
Daniel Gratzer

1
@jozefg cây được lấp đầy một phần là một biểu diễn thích hợp của một tính toán như một biểu thức, nhưng vào cuối ngày, mã thực tế được viết là một biểu thức của các loại không thể nhận dạng khác với thủ tục, như tôi hiểu nó. Điều đó sang một bên, cây đầy một phần là đại diện trình biên dịch; đại diện nhà phát triển dự kiến ​​là một biểu thức tính toán quy định với cú pháp và ngữ nghĩa của ngôn ngữ, sẽ xuất hiện với 99% các nhà phát triển dưới dạng "thủ tục", "hàm" hoặc cách khác. Bạn đang suy nghĩ từ quan điểm của nhà phát triển trình biên dịch heh
Jimmy Hoffa

2

Một ví dụ bạn có thể thích là coroutines. Ví dụ, các Coroutines từ Lua hoặc các trình lặp / trình tạo từ Python hoặc C # có sức mạnh tương tự như các liên tục một lần bắn (các phần tiếp theo mà bạn chỉ được phép gọi một lần) nhưng việc tiếp tục không được thực hiện rõ ràng thành một chức năng. Thay vào đó, bạn có cách để tiến tới coroutine cho đến khi tuyên bố "nhường" tiếp theo.

Ví dụ, hãy xem xét chương trình Python sau:

def my_iterator():
   yield 1
   yield 2
   yield 3

def main():
   it = my_iterator()
   x = it.next()
   y = it.next()
   z = it.next()
   print x + y + z

Nó tương tự như chương trình Javascript sau với các cuộc gọi lại rõ ràng:

function my_iterator()
  return function(cb1){
    cb1(1, function(cb2){
      cb2(2, function(cb3){
        cb3(3, function(cb4){
          throw "stop iteration";
        });
      });
    });
  });
}

function main(){
   var getNext1 = my_iterator();
   getNext1(function(x, getNext2){
      getNext2(function(y, getNext3){
         getNext3(function(z, getNext4){
            console.log(x + y + z);
         });
      });
   });
}

Ví dụ Javascript khá ồn vì mỗi bước cần trả về phần tiếp theo bên cạnh việc trả về giá trị mang lại (trong Python theo dõi phần tiếp theo bên trong ite

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.