Zobacz Programming Languages, Application and Interpretation, zaczynając od rozdziału 15. Rozdział 18 mówi o tym, jak zrobić to automatycznie, ale jeśli nie jesteś zaznajomiony z myśleniem o wyrażaniu funkcji, która robi "co dalej", prawdopodobnie będziesz chciał spróbuj najpierw ćwiczeń palcem.
Nie pozwól, aby ktoś zrobił to za ciebie: naprawdę będziesz chciał zrozumieć proces i być w stanie zrobić to ręcznie, niezależnie od Planu lub w inny sposób. Pojawia się szczególnie w programowaniu w języku Asynchronicznym JavaScript, gdzie naprawdę nie masz innego wyboru, jak zrobić transformację.
W CPS przekształcić wszystkie non-prymitywne funkcje muszą teraz konsumować funkcję, która reprezentuje "co-to-do-next". Obejmuje to wszystkie lambdy. Symetrycznie, każda aplikacja funkcji nie-prymitywnej musi zapewniać funkcję "co do zrobienia dalej", a resztę obliczeń w tej funkcji.
Więc jeśli mieliśmy program do obliczania trójkąta hypothenuse:
(define (hypo a b)
(define (square x) (* x x))
(define (add x y) (+ x y))
(sqrt (add (square a)
(square b))))
i jeżeli stwierdzamy, że tylko prymitywne wnioski oto *
, +
i sqrt
, to wszystkie inne definicje funkcji i wywołania funkcji muszą być tłumaczone, tak:
(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)))
ostatni wyrażenie pokazuje, że kończy się konieczności obliczania „inside-out”, i że transformacja jest wszechobecna: wszystkie lambdy w źródłowym programie źródłowym wymaga dodatkowego argumentu, a wszystkie aplikacje niepochodzące z prymitywów muszą nazywać "co dalej" jako ten argument.
Przyjrzyj się dokładnie sekcji 17.2 cytowanej książki: obejmuje to, a także 17,5, która mówi o tym, dlaczego musisz dotknąć WSZYSTKICH lambd w programie źródłowym, aby przypadek wyższego rzędu działał .
Jako inny przykład transformacji, zastosowano dla przypadku wyższego rzędu, powiedzmy, że mamy:
(define (twice f)
(lambda (x)
(f (f x))))
Wtedy tłumaczenie coś takiego jest:
(define (twice/k f k1)
(k1 (lambda ...)))
... ponieważ lambda to tylko wartość, którą można przekazać k1
. Ale oczywiście tłumaczenie również musi przebiegać przez lambdę.
Najpierw musimy wykonać wewnętrzne połączenie z numerem f
z x
(pamiętajmy, że wszystkie niepochodzące z prymitywów aplikacje funkcyjne muszą przekazać odpowiednie "co dalej"!„):
(define (twice/k f k1)
(k1 (lambda (x k2)
(f x (lambda (fx-val)
...)))))
... wziąć tę wartość i zastosować go ponownie do f ...
(define (twice/k f k1)
(k1 (lambda (x k2)
(f x (lambda (fx-val)
(f fx-val ...))))))
... i wreszcie powrócić tę wartość do 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)))))
Przykro mi, to mi nie pomaga. Dziękuję i tak za próbę. –
Z jaką częścią masz problemy? Czy wiesz, jak przekształcić prostą funkcję, taką jak (lambda (x) (+ x 1)) w styl CPS? Bardzo trudno jest ci pomóc, bez mentalnego modelu, w którym utkniesz. Czy przejrzałeś te rozdziały w cytowanej książce, czy nie masz czasu? – dyoo
Tak, mam i wiem, jak przekształcić "proste" procedury, takie jak (lambda (x) (+ x 1)), ale co, jeśli "x" jest samą procedurą? jak (lambda (x) (x 1))? Muszę przekształcić każdą procedurę zdefiniowaną przez użytkownika, prawda? –