2009-02-27 22 views
23

Sto provando a creare una funzione al volo che restituisca un valore costante.Come si fanno le chiusure in Emacs Lisp?

In JavaScript e altri linguaggi imperativi moderni userei chiusure:

function id(a) { 
    return function() {return a;}; 
} 

ma Emacs Lisp non supporta quelli.

Posso creare un mix di funzione di identità e applicazione di funzione parziale ma non è supportato neanche.

Quindi come faccio?

+0

Per quanto ho sentito, JavaScript è in realtà piuttosto funzionale. – Svante

+0

Dipende dal punto di vista di una persona. Per me, se la maggior parte del codice nella lingua è imperativa, è imperativo. Qual è il caso qui. – vava

+0

A partire dalla versione 24, Emacs ora ha uno scope lessicale. –

risposta

8

un'idea stupida: come circa:

(defun foo (x) 
    `(lambda() ,x)) 

(funcall (foo 10)) ;; => 10 
+1

Si interrompe quando si desidera scrivere qualcosa come: (lessicale-let ((a 0)) (cons (lambda() a) (lambda (new-a) (setf a new-a)))) – jrockway

28

trovato un'altra soluzione con lessicale-let

(defun foo (n) 
    (lexical-let ((n n)) #'(lambda() n))) 

(funcall (foo 10)) ;; => 10 
6

non mi fermo in Emacs Lisp, ma per quanto ne so, una grande differenza da Common Lisp è che utilizza l'ambito dinamico in tutto. Lo Emacs Lisp Manual afferma che Emacs Lisp non ha chiusure.

Proverò ad applicare le mie conoscenze teoriche di ambito dinamico.

Se si dispone di una funzione id, che restituisce solo il valore di my-id:

 
(defun id() 
    my-id) 

e lo si utilizza in qualche altra funzione:

 
(defun some-other-place() 
    (id)) 

e da qualche parte sulla strada per la chiamata di id si lega my-id tramite ad es. una delusione:

 
(defun even-elsewhere() 
    (let ((my-id 5)) 
    (some-other-place))) 

questo dovrebbe restituire 5.

So che scoping dinamico è una bestia strana, quando si è abituati a scoping lessicale, ma forse è possibile utilizzare questo per implementare il comportamento desiderato.

+0

Wow , è bello :) – vava

7

Solo il lisp di Emacs ha uno scope dinamico. C'è una macro lexical-let che approssima lo scoping lessicale attraverso un hack piuttosto terribile.

+9

Ovviamente, il "trucco piuttosto terribile" è ciò che accade sotto le copertine di altre implementazioni linguistiche. – jrockway

4

Emacs 24 ha lessicale vincolante.

http://www.emacswiki.org/emacs/LexicalBinding

+0

Fate riferimento anche alla sezione del manuale GNU Emacs su [Variable Scoping] (https://www.gnu.org/software/emacs/manual/html_node/elisp/Variable-Scoping.html#Variable-Scoping) – dat

12

reale (non falsi) Le chiusure a Emacs 24.

Sebbene Emacs 24 presenta scavare lessicale quando la variabile lessicale vincolante ha valore t, il defun speciale la forma non funziona correttamente in contesti legati lessicalmente (almeno non in Emacs 24.2.1.) Questo rende difficile, ma non è impossibile da definire reale (non falso) chiusure.Per esempio:

(let ((counter 0)) 
    (defun counting() 
    (setq counter (1+ counter)))) 

non funzionerà come previsto perché il contatore simbolo nel defun sarà legato alla variabile globale di questo nome, se ce n'è uno, e non la variabile lessicale definire nel let. Quando viene chiamata la funzione conteggio, se la variabile globale non esiste, allora ovviamente fallirà. Hoever se c'è una tale variabile globale viene aggiornata, il che probabilmente non è ciò che era previsto e potrebbe essere un bug difficile da rintracciare poiché la funzione potrebbe sembrare funzionare correttamente.

Il byte compilatore fa dare un avvertimento, se si utilizza defun in questo modo e presumibilmente il problema verrà affrontato in qualche futura versione di Emacs, ma fino ad allora la seguente macro può essere utilizzato:

(defmacro defun** (name args &rest body) 
    "Define NAME as a function in a lexically bound context. 

Like normal `defun', except that it works correctly in lexically 
bound contexts. 

\(fn NAME ARGLIST [DOCSTRING] BODY...)" 
    (let ((bound-as-var (boundp `,name))) 
    (when (fboundp `,name) 
     (message "Redefining function/macro: %s" `,name)) 
    (append 
    `(progn 
     (defvar ,name nil) 
     (fset (quote ,name) (lambda (,@args) ,@body))) 
    (if bound-as-var 
     'nil 
     `((makunbound `,name)))))) 

Se si definisce contando come segue:

(let ((counter 0)) 
    (defun** counting() 
    (setq counter (1+ counter)))) 

che funzionerà come previsto e aggiornare la variabile legata lessicalmente contano ogni volta che viene richiamato, mentre restituisce il nuovo valore.

avvertimento: La macro potrebbe non funzionare correttamente se si tenta di defun ** una funzione con lo stesso nome di una delle variabili lessicalmente legate. Se si fa qualcosa del tipo:

(let ((dont-do-this 10)) 
    (defun** dont-do-this() 
    ......... 
    .........)) 

Non riesco a immaginare che qualcuno lo faccia effettivamente ma ne valeva la pena.

Nota: ho chiamato la macro defun **in modo che non si scontra con la macro defun * nel pacchetto cl, tuttavia non dipende in alcun modo su quella pacchetto.

+0

Potete dare un esempio completo? Sembra che il codice (let ((contatore 0)) (defun ** counting() (contatore setq (contatore 1+)))) non funzioni come previsto. – toolchainX

+1

La limitazione alla definizione delle chiusure con 'defun' è stata risolta in Emacs-24.3 (dove' defun' è ora definito come una macro piuttosto che una forma speciale). Quindi dal momento che 24.3 'defun' funziona come la tua macro' defun ** '(tho senza il broken' (defvar, name nil) 'e fissa vari altri aspetti secondari minori come l'uso di' fset' piuttosto che 'defalias' e la gestione di il cosiddetto "dynamic docstring" che richiedeva modifiche nel bytecompiler). – Stefan

Problemi correlati