2011-10-28 13 views
5

Schemi o qualsiasi dialetto di schema hanno un tipo di operatore "self" in modo che i lambda anonimi possono ricorrere su se stessi senza fare qualcosa come un combinatore Y o essere nominati in un letrec ecc.Lambdan anonimi che si riferiscono direttamente a se stessi

Qualcosa di simile:

(lambda (n) 
    (cond 
    ((= n 0) 1) 
    (else (* n (self (- n 1))))))) 

risposta

9

No. il problema con l'approccio "lambda corrente" è che Scheme ha molti lambda nascosti. Ad esempio:

  • Tutte le let forme (compresi let*, letrec e denominato let)
  • do (che si espande in una chiamata let)
  • delay, lazy, receive, ecc

Per richiedere al programmatore di sapere qual è il più interno lambda sarebbe interrompere l'incapsulamento, nel senso che avresti per sapere dove sono tutti i lambda nascosti, e gli autori di macro non possono più usare lambda come un modo per creare un nuovo ambito.

Perdita a tutto tondo, se me lo chiedi.

+2

Ascolta, ascolta. Nota che questo è anche un problema con "return" come appare nella maggior parte delle lingue (Java, C, ecc.). –

+0

Credo che tecnicamente si possa rendere l'identificatore 'self' lessicale rispetto alla forma lambda. Puoi persino farlo da solo se hai "syntax-case". Sono d'accordo comunque, comunque. I nomi espliciti sono generalmente preferibili a quelli anaforici. –

+1

@Matthias: In effetti, "può" e "dovrebbe" sono totalmente diversi, specialmente in questo contesto. ;-) Mi stavo occupando del perché fornire un 'sé' di default è una pessima idea, in generale. –

6

C'è una tradizione di scrivere macro "anaforiche" che definiscono nomi speciali nella portata lessicale dei loro corpi. Utilizzando syntax-case, è possibile scrivere una tale macro sopra letrec e lambda. Si noti che la definizione di seguito è il più igienica possibile considerando le specifiche (in particolare, gli usi invisibili di alambda non ombreggiano 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 

La maggior parte delle persone evita gli operatori anaforici poiché rendono meno riconoscibile la struttura del codice. Inoltre, il refactoring può introdurre problemi piuttosto facilmente. (Considera cosa succede quando avvolgi il modulo let/alambda nella funzione fattoriale sopra in un altro modulo alambda. È facile trascurare gli usi di self, specialmente se non ti viene ricordato che è rilevante doverlo digitare esplicitamente.) È quindi generalmente preferibile usare nomi espliciti. Una versione “etichetta” di lambda che permette questo può essere definita utilizzando un semplice syntax-rules macro:

;; 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

Il tuo 'llambda' è quasi identico a [SRFI 31] (http://srfi.schemers.org/srfi-31/srfi-31.html) 'rec', e il secondo è il nome solito per il costrutto . :-) –

+0

@ ChrisJester-Young Ah, interessante. Dovrei sfogliare più spesso l'elenco SRFI. ;) 'rec' sembra anche essere più generale; in effetti, sembra una generalizzazione dell'operatore 'label' dell'antica LISP (che è stata la prima cosa che mi è venuta in mente quando ho letto la domanda dell'OP). –

+1

Due cose. Per prima cosa, specialmente nel contesto di Racket, c'è un [modo migliore] (http://blog.racket-lang.org/2008/02/dirty-looking-hygiene.html) per farlo. In secondo luogo, il problema con le macro anaforiche a cui ti stai riferendo (scope nidificato per il nome implicito) non è il problema più grande. –

0

ho trovato un modo utilizzando continuazioni di avere lambda anonimi si dicono e quindi utilizzando le macro Racket per mascherare la sintassi in modo che il lambda anonima sembra avere un operatore "autonomo". Non so se questa soluzione sia possibile in altre versioni di Scheme poiché dipende dalla funzione di continuazione chiamata-composable di racket e la macro per nascondere la sintassi utilizza i parametri di sintassi.

L'idea di base è questa, illustrata con la funzione fattoriale.

((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) 

La continuazione k è la chiamata alla funzione fattoriale anonima, che prende due argomenti, il primo essendo la continuazione stessa. In modo che quando nel corpo eseguiamo (k k N) che è equivalente alla funzione anonima che si chiama (nello stesso modo che farebbe un lambda ricorsivo chiamato).

Quindi mascheriamo il modulo sottostante con una macro. RACCHETTE sintassi parametri permettono la trasformazione di auto (args ...) a (kk ARGS ...)

modo che possiamo avere:

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

Il programma completo Racket per fare questo è:

#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) 

Questo risponde anche alla mia precedente domanda sulle differenze tra i diversi tipi di continuazioni. Different kinds of continuations in Racket

questo funziona solo perché a differenza di call-con-corrente di continuazione, call-con-componibile di continuazione di non interrompere di nuovo ad un prompt di continuità, ma invoca la continuazione nel luogo è stato invocato.

+0

Ehm, ma questo non è correlato alla tua domanda originale ... –

+0

Guardando la domanda originale hai ragione, ma ho visto questo come un modo per simulare o aggiungere un operatore "auto" a un lambda. modo di farlo. –

Problemi correlati