2011-10-28 13 views

Odpowiedz

9

nr kłopot z podejściem „prąd lambda” jest to, że Program ma wiele ukryte lambdy. Na przykład:

  • Wszystkie let formularzy (w tym let*, letrec i nazwał let)
  • do (który rozszerza się nazwanego let)
  • delay, lazy, receive itp

Aby wymagać od programisty, aby dowiedzieć się, jaka jest najgłębsza lambda, należy przerwać hermetyzację. aby wiedzieć, gdzie są wszystkie ukryte lambdy, a pisarze makr nie mogą już używać lambd jako sposobu na stworzenie nowego zakresu.

Dookoła przegrywa, jeśli mnie pytasz.

+2

Usłysz, posłuchaj. Zauważ, że jest to również problem z "return", ponieważ pojawia się w większości języków (Java, C, itp.). –

+0

Wierzę, że pod względem technicznym można by utworzyć identyfikator "ja" w odniesieniu do formy lambda. Możesz nawet zrobić to sam, jeśli masz 'case-składni'. Zgadzam się mimo wszystko. Nazwy jawne są na ogół lepsze niż te z nazwami anaforycznymi. –

+1

@ Matthias: Rzeczywiście, "może" i "powinien" są całkowicie różne, szczególnie w tym kontekście. ;-) Zastanawiałem się, dlaczego domyślnie podanie "ja" jest ogólnie rzecz biorąc okropnym pomysłem. –

6

Istnieje tradycja pisania makr "anaforycznych", które definiują specjalne nazwy w zakresie leksykalnym ich ciał. Używając syntax-case, możesz zapisać takie makro na letrec i lambda. Zauważ, że poniższa definicja jest maksymalnie higieniczna, biorąc pod uwagę specyfikację (w szczególności niewidoczne zastosowania alambda nie będą cieniem self).

;; Define a version of lambda that binds the 
;; anaphoric variable “self” to the function 
;; being defined. 
;; 
;; Note the use of datum->syntax to specify the 
;; scope of the anaphoric identifier. 
(define-syntax alambda 
    (lambda (stx) 
    (syntax-case stx() 
     [(alambda lambda-list . body) 
     (with-syntax ([name (datum->syntax #'alambda 'self)]) 
     #'(letrec ([name (lambda lambda-list . body)]) 
      name))]))) 

;; We can define let in terms of alambda as usual. 
(define-syntax let/alambda 
    (syntax-rules() 
    [(_ ((var val) ...) . body) 
    ((alambda (var ...) . body) val ...)])) 

;; The let/alambda macro does not shadow the outer 
;; alambda's anaphoric variable, which is lexical 
;; with regard to the alambda form. 
((alambda (n) 
    (if (zero? n) 
     1 
     (let/alambda ([n-1 (- n 1)]) 
     (* (self n-1) n)))) 
10) 
;=> 3628800 

Większość ludzi unika operatorów anaforycznych, ponieważ sprawiają, że struktura kodu jest mniej rozpoznawalna. Ponadto, refaktoryzacja może dość łatwo wprowadzić problemy. (Zastanów się, co stanie się, gdy zawiniesz formularz let/alambda w funkcji silniku powyżej w innym formularzu alambda. Łatwo przeoczyć zastosowania self, zwłaszcza jeśli nie przypominasz sobie o tym, że jest on istotny, ponieważ musisz go jawnie wpisać.) Dlatego też zazwyczaj lepiej jest używać jawnych nazw. A „oznaczony” wersja lambda że pozwala to można zdefiniować za pomocą prostego syntax-rules makro:

;; Define a version of lambda that allows the 
;; user to specifiy a name for the function 
;; being defined. 
(define-syntax llambda 
    (syntax-rules() 
    [(_ name lambda-list . body) 
    (letrec ([name (lambda lambda-list . body)]) 
     name)])) 

;; The factorial function can be expressed 
;; using llambda. 
((llambda fac (n) 
    (if (zero? n) 
     1 
     (* (fac (- n 1)) n))) 
10) 
;=> 3628800 
+2

Twoja 'llambda' jest prawie identyczna z' rec '[SRFI 31] (http://srfi.schemers.org/srfi-31/srfi-31.html) 'rec', a druga jest zwykle nazwą dla konstruktu . :-) –

+0

@ ChrisJester-Young Ah, interesujące. Powinienem częściej przeglądać listę SRFI. ;) "rec" wydaje się nawet bardziej ogólne; w rzeczywistości wygląda to na uogólnienie operatora 'label' starożytnego LISP-a (co było pierwszą rzeczą, o której myślałem po przeczytaniu pytania OP). –

+1

Dwie rzeczy. Po pierwsze, szczególnie w kontekście rakietowym jest [lepszy sposób] (http://blog.racket-lang.org/2008/02/dirty-looking-hygiene.html), aby to zrobić. Po drugie, problem z anaforycznymi makrami, do których się odwołujesz (zagnieżdżony zakres dla niejawnej nazwy) nie jest największym problemem. –

0

znalazłem sposób przy użyciu kontynuacje mieć anonimowe lambdy nazywają siebie, a następnie przy użyciu makr rakieta do ukrycia składni dlatego uwagi anonimowy lambda wydaje się mieć "ja" operatora. Nie wiem, czy to rozwiązanie jest możliwe w innych wersjach Schematu, ponieważ zależy to od funkcji kontynuacji połączenia z kompozycją i Makro, aby ukryć składnię, używając parametrów składni.

Podstawowa idea jest taka, zilustrowana funkcją silni.

((lambda (n) 
    (call-with-values 
     (lambda() (call-with-composable-continuation 
         (lambda (k) (values k n)))) 
    (lambda (k n) 
     (cond 
      [(= 0 n) 1] 
      [else (* n (k k (- n 1)))])))) 5) 

Kontynuacja k jest wywołanie funkcji anonimowej silni, który trwa dwa argumenty, z których pierwszym jest sama kontynuacja. Tak więc, gdy w ciele wykonujemy (k k N), który jest równoważny z anonimową funkcją wywołującą samą siebie (w taki sam sposób, jak zrobiłaby to rekursywna o nazwie lambda).

Następnie ukrywamy podstawowy formularz za pomocą makra. Rakiety składni parametry umożliwiają transformację (self args ...) do (kk ARG ...)

więc możemy mieć:

((lambda-with-self (n) 
    (cond 
     [(= 0 n) 0] 
     [(= 1 n) 1] 
     [else (* n (self (- n 1)))])) 5) 

Kompletny program rakieta to zrobić:

#lang racket 
(require racket/stxparam) ;required for syntax-parameters 
( define-syntax-parameter self (λ (stx) (raise-syntax-error #f "not in `lambda-with-self'" stx))) 

(define-syntax-rule 
(lambda-with-self (ARG ...) BODY ...) 
(lambda (ARG ...) 
    (call-with-values 
    (lambda()(call/comp (lambda (k) (values k ARG ...)))) 
    (lambda (k ARG ...) 
     (syntax-parameterize ([self (syntax-rules ()[(self ARG ...) (k k ARG ...)])]) 
    BODY ...))))) 
;Example using factorial function 
((lambda-with-self (n) 
     (cond 
     [(= 0 n) 0] 
     [(= 1 n) 1] 
     [else (* n (self (- n 1)))])) 5) 

To również odpowiada na moje poprzednie pytanie o różnice między różnymi rodzajami kontynuacji. Different kinds of continuations in Racket

ten działa tylko dlatego, że w przeciwieństwie do call-z-current-kontynuacja, call-z-sk-kontynuacja nie przerwie z powrotem do wiersza kontynuacja, ale wywołuje kontynuację w miejscu został wywołany.

+0

Hm, ale to nie ma związku z pierwotnym pytaniem ... –

+0

Patrząc na oryginalne pytanie masz rację, ale widziałem to jako sposób na symulację lub dodanie operatora "ja" do lambda. sposób robienia tego. –

Powiązane problemy