Dlaczego kontynuacja stylu przechodzenia

W Język programowania Scheme Autor: Kent Dybvig (4th edition) sekcja 3.4 , opisuje bardzo jasno Czym jest kontynuacja stylu przechodzenia. Dla Dlaczego podaje dwa powody:

  1. przekazać więcej niż jeden wynik do jego kontynuacji, ponieważ procedura implementująca kontynuację może przyjmować dowolną liczbę argumentów.
  2. CPS pozwala również na oddzielne kontynuacje procedury ..., które mogą akceptować różne liczby argumentów.

Ponieważ pierwszy powód może być również wykonany za pomocą procedury values, a drugi za pomocą case-lambda, nie jestem jasne korzyści z używania stylu continuation passing. Czy ktoś mógłby mi pokazać kilka przykładów, gdzie styl kontynuowania jest odpowiedni, gdzie czyni kod lepszym, jaśniejszym itp.?

Author: Joshua Taylor, 2011-12-17

2 answers

Dybvig wykorzystuje wyraźne kontynuacje w tej sekcji, aby motywować posiadanie call/cc jako część języka. Główny punkt znajduje się na końcu sekcji, kiedy wspomina, że pisanie kodu bez niego wymaga globalnej transformacji całego kodu, który jest używany, w tym funkcji, które wywołujesz. Tak więc w Scheme zazwyczaj budujesz swój własny konstruktor używając makr, a kontynuacje są jednym z tych użytecznych konstruktów -- ale nie możesz ich zaimplementować za pomocą makr, ponieważ implementują one tylko lokalne transformacje.

Ale używanie bezpośrednio stylu CPS może być nadal użyteczne: na przykład, jak wspomina, można napisać funkcję, która ma więcej niż jedną kontynuację, być może z różnymi arrities-jak funkcja parsująca, która otrzymuje funkcję z jednym wejściem, aby wysłać wartość parsowania i zerową funkcję do wywołania podczas parsowania fail (I Ta funkcja może przerwać z błędem lub backtrack i spróbować użyć innych reguł parsowania). Innym możliwym zastosowaniem jest kontrolowanie dokładnie to, co idzie do kontynuacji, zamiast pozwolić call/cc chwycić pełny kontekst.

Istnieje również oczywisty przypadek pisania kodu w języku, który nie ma kontynuacji pierwszej klasy, czyniąc Kod CPSed jedynym wyborem. Przykładem tego może być wiele węzłów.programy js, które używają IO i prawie zmuszają do pisania kodu w jawnych CPS.

 13
Author: Eli Barzilay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-17 15:38:09

Ponieważ pierwszy powód może być również wykonany za pomocą procedury values, a drugi za pomocą case-lambda nie jestem jasne korzyści z używania stylu continuation passing.

...z tym wyjątkiem, że definicja values Określa, że nazywa ona swoją kontynuację z wieloma argumentami.

Moim ulubionym przykładem problemu, w którym styl przechodzenia jest pomocny, jest pisanie matcherów wzorców. Jest to rodzaj makro, które jest jak case na sterydach; przyjmuje wartość i próbuje dopasować swoją strukturę do sekwencji wzorców zbudowanych z par, symboli (stojących dla zmiennych) i atomów nie-symbolicznych (stojących dla wartości). Jeśli dopasowanie się powiedzie, to wiąże identyfikatory we wzorcu z odpowiadającymi im podczęściami wartości i wykonuje wynik dla tego wzorca. Jeśli się nie powiedzie, to spróbuje następnego wzoru.

Jest to dość proste, aby napisać tego rodzaju makro w formie kontynuacji stylu przechodzenia, przy użyciu dwóch różnych kontynuacje reprezentujące "co zrobić, jeśli mecz się powiedzie" (kontynuacja sukcesu) i "co zrobić, jeśli mecz się nie powiedzie" (kontynuacja porażki).

Weź ten (uproszczony) fragment makra pasującego do wzorca, który kiedyś napisałem (przepraszam, jeśli nie znasz składni-case lub składni-rules; a ponieważ zaadaptowałem go w locie, mam nadzieję, że też zadziała!). Skupię się na zasadzie, która pasuje do wzoru pary. Jest to wzór, który składa się z pary dwóch wzorów, wzoru głowy i wzór ogona; pasuje do par, których głowa pasuje do wzoru głowy i których ogon pasuje do wzoru ogona.

;;;
;;; Outer "driver" macro; the meat is in pmatch-expand-pattern.
;;;
(define-syntax pmatch
  (syntax-rules ()
    ((pmatch value-expr (pattern . exprs) . clauses)
     (let* ((value value-expr)
            (try-next-clause
             (lambda () (pmatch value . clauses))))
       (pmatch-expand-pattern pattern
                              value
                              ;; success-k
                              (begin . exprs)
                              ;; failure-k
                              (try-next-clause))))))

(define-syntax pmatch-expand-pattern
  (lambda (stx)
    (syntax-case stx ()

      ;; Cases for constants and quoted symbols omitted, but they're trivial.

      ;; Match a pair pattern.  Note that failure-k is expanded three times; 
      ;; that's why pmatch encapsulates its expansion inside a thunk!
      ((pmatch-expand-pattern (head-pat . tail-pat) value success-k failure-k)
       (syntax
        (if (pair? value)
            (pmatch-expand-pattern head-pat 
                                   (car value)
                                   ;; If we successfully match the head, then
                                   ;; the success continuation is a recursive
                                   ;; attempt to match the tail...
                                   (pmatch-expand-pattern tail-pat
                                                          (cdr value)
                                                          success-k 
                                                          failure-k)
                                   failure-k))
            failure-k))

      ;; Match an identifier pattern.  Always succeeds, binds identifier
      ;; to value
      ((pmatch-expand-pattern identifier value success-k failure-k)
       (identifier? (syntax identifier))
       (syntax (let ((identifier value)) success-k)))
      )))

Zwróć uwagę na podformy success-k i failure-k w makro wyrażeniach pmatch-expand-pattern. Reprezentują one wyrażenia, które są używane jako" kontynuacja", w nieco luźnym określeniu, dla pattern matcher. Kontynuacja sukcesu jest używana, gdy rozważany wzorzec pasuje do rozważanej wartości; kontynuacja niepowodzenia jest używana, gdy nie. sukces kontynuacją jest, w zależności od tego, czy dopasowaliśmy wszystkie obecne wzorce najwyższego poziomu, wyrażenie, które będzie pasować do reszty wzorca, lub wynik, który wykonujemy po dopasowaniu wzorca. Kontynuacje awarii są używane, gdy wzorzec nie pasuje, w celu powrotu do następnego punktu wyboru.

Jak wspomniałem, termin "kontynuacja" jest używany nieco luźno w powyższym kodzie, ponieważ używamy wyrażeń jako kontynuacji. Ale to tylko szczegół o tym, jak zaimplementować to jako makro-algorytm może być zaimplementowany wyłącznie w czasie wykonywania z rzeczywistymi procedurami jako kontynuacjami. (Również procedury try-next-clause są kontynuacjami w dosłownym znaczeniu.)

 6
Author: Luis Casillas,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-12-19 19:46:31