Xem Programming Languages, Application and Interpretation, bắt đầu từ Chương 15. Chương 18 nói về cách tự động thực hiện, nhưng nếu bạn không quen với suy nghĩ về cách diễn đạt chức năng "làm gì tiếp theo", có thể bạn sẽ muốn thử các bài tập ngón tay trước.
Không ai đó làm điều đó cho bạn: bạn thực sự muốn hiểu quy trình và có thể làm điều đó bằng tay, độc lập với Đề án hoặc cách khác. Nó xuất hiện đặc biệt trong lập trình web không đồng bộ JavaScript, nơi bạn thực sự không có lựa chọn nào ngoài việc thực hiện phép biến đổi.
Trong phép biến đổi CPS, tất cả các chức năng không nguyên thủy cần phải tiêu thụ một hàm đại diện cho "làm gì tiếp theo". Điều đó bao gồm tất cả lambdas. Đối xứng, bất kỳ ứng dụng nào của một hàm không nguyên thủy cần phải cung cấp một hàm "gì-to-do-next", và phần còn lại của tính toán trong hàm đó.
Vì vậy, nếu chúng ta có một chương trình để tính toán hypothenuse của một tam giác:
(define (hypo a b)
(define (square x) (* x x))
(define (add x y) (+ x y))
(sqrt (add (square a)
(square b))))
và nếu chúng ta nói rằng các ứng dụng nguyên thủy duy nhất ở đây là *
, +
, và sqrt
, sau đó tất cả các định nghĩa chức năng khác và các cuộc gọi chức năng cần được dịch, như sau:
(define (hypo/k a b k)
(define (square/k x k)
(k (* x x)))
(define (add/k x y k)
(k (+ x y)))
(square/k a
(lambda (a^2)
(square/k b
(lambda (b^2)
(add/k a^2 b^2
(lambda (a^2+b^2)
(k (sqrt a^2+b^2)))))))))
;; a small test of the function.
(hypo/k 2 3 (lambda (result) (display result) (newline)))
Biểu thức cuối cùng cho thấy bạn phải tính toán "từ trong ra ngoài" và chuyển đổi phổ biến: tất cả lambdas trong chương trình nguồn gốc kết thúc cần phải có một đối số bổ sung, và tất cả các ứng dụng không nguyên thủy cần phải nhồi nhét "những gì cần làm tiếp theo" làm đối số đó.
Xem kỹ phần 17.2 của sách được trích dẫn: nó bao gồm điều này, cũng như 17.5, nói về lý do tại sao bạn cần chạm vào TẤT CẢ các lambdas trong chương trình nguồn để trường hợp thứ tự cao hơn cũng hoạt động .
Như một ví dụ về sự biến đổi, áp dụng cho một trường hợp bậc cao, giả sử rằng chúng ta có:
(define (twice f)
(lambda (x)
(f (f x))))
Sau đó, bản dịch của một cái gì đó như thế này là:
(define (twice/k f k1)
(k1 (lambda ...)))
... bởi vì lambda đó chỉ là một giá trị có thể được chuyển đến k1
. Nhưng tất nhiên, bản dịch cũng cần phải chạy qua lambda.
Trước tiên, chúng tôi phải thực hiện cuộc gọi nội bộ tới f
với x
(và nhớ rằng tất cả các ứng dụng chức năng không nguyên thủy cần phải chuyển "thích hợp làm gì tiếp theo" thích hợp!"):
(define (twice/k f k1)
(k1 (lambda (x k2)
(f x (lambda (fx-val)
...)))))
... mất giá trị đó và áp dụng nó một lần nữa để e ...
(define (twice/k f k1)
(k1 (lambda (x k2)
(f x (lambda (fx-val)
(f fx-val ...))))))
... và cuối cùng trở về giá trị đó để k2
:
(define (twice/k f k1)
(k1 (lambda (x k2)
(f x (lambda (fx-val)
(f fx-val k2))))))
;; test. Essentially, ((twice square) 7)
(define (square/k x k) (k (* x x)))
(twice/k square/k
(lambda (squaresquare)
(squaresquare 7
(lambda (seven^4)
(display seven^4)
(newline)))))
Tôi xin lỗi, điều này không giúp tôi. Cảm ơn bạn đã thử. –
Bạn đang gặp phải vấn đề gì? Bạn có biết cách chuyển đổi một hàm đơn giản, chẳng hạn như (lambda (x) (+ x 1)) sang kiểu CPS không? Nó rất khó để giúp đỡ, không có mô hình tinh thần của nơi bạn đang gặp khó khăn. Bạn đã trải qua những chương đó trong cuốn sách được trích dẫn hay bạn không có thời gian? – dyoo
Có, tôi có, và tôi biết cách chuyển đổi các thủ tục "đơn giản" như (lambda (x) (+ x 1)), nhưng nếu 'x' là thủ tục thì sao? như (lambda (x) (x 1))? Tôi cần phải biến đổi mọi thủ tục được xác định bởi người dùng phải không? –