2009-02-27 16 views
23

Próbuję utworzyć funkcję w locie, która zwróci jedną stałą wartość.Jak zrobić zamknięcia w Emacs Lisp?

W JavaScript i innych języków nowożytnych nadrzędnymi użyłbym zamknięć:

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

ale Emacs LISP nie obsługuje tych.

Mogę utworzyć mieszankę funkcji identyfikacji i aplikacji funkcji częściowych, ale nie jest ona obsługiwana.

Jak to zrobić?

+0

O ile słyszałem, JavaScript jest raczej funkcjonalny. – Svante

+0

To zależy od punktu widzenia. Dla mnie, jeśli większość kodu w języku jest niezbędna, jest to konieczne. Tak jest w tym przypadku. – vava

+0

Począwszy od wersji 24, Emacs ma teraz zakres leksykalny. –

Odpowiedz

8

głupi pomysł: jak about:

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

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

To zepsuło się, gdy chcesz napisać coś w stylu: (leksykalny-let ((a 0)) (cons (lambda() a) (lambda (nowy-a) (setf a new-a)))) – jrockway

28

znaleziono inne rozwiązanie z leksykalno-let

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

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

nie jestem mocny w Emacs Lisp, ale o ile wiem, to duża różnica od Common Lisp jest to, że używa dynamicznego zakresu w całym tekście. Emacs Lisp Manual stwierdza, że ​​Emacs Lisp nie ma zamknięć.

Spróbuję zastosować moją teoretyczną wiedzę na temat dynamicznego zakresu.

Jeśli masz funkcję id które po prostu zwraca wartość my-id:

 
(defun id() 
    my-id) 

i użyć go w innej funkcji:

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

i gdzieś po drodze na wezwanie id wiążesz my-id poprzez np. zawód:

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

to powinien oddać 5.

wiem, że dynamiczna analiza zakresu jest dziwne zwierzę, gdy są stosowane w celu określenia zakresu leksykalnego, ale być może można to wykorzystać, aby realizować swoje pożądane zachowanie.

+0

Wow , to fajne :) – vava

7

Emps seplenie ma tylko zakres dynamiczny. Istnieje makro lexical-let przybliżające zakresy leksykalne przez dość hakera.

+9

Oczywiście, "raczej straszny hack" jest tym, co dzieje się pod osłonami innych implementacji językowych. – jrockway

4

Emacs 24 posiada leksykalny wiążące.

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

+0

Odwołaj się także do rozdziału Podręcznika GNU Emacs na temat [Zmienna Scoping] (https://www.gnu.org/software/emacs/manual/html_node/elisp/Variable-Scoping.html#Variable-Scoping) – dat

12

Real (sfałszować) zamknięć Emacs 24.

Chociaż Emacsa 24 ma leksykograficznego czerpania gdy zmienna słownikowa wiążące ma wartość t The defun specjalny formularz nie działa poprawnie w kontekście leksykalnym (przynajmniej nie w Emacs 24.2.1.) Utrudnia to, ale nie jest możliwe, ale definiuje rzeczywiste (nie fałszywe) zamknięcia.Na przykład:

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

nie będzie działać zgodnie z oczekiwaniami, ponieważ licznik w defun symbol będzie związany z globalną zmienną o tej nazwie, jeżeli jest tylko jedna, a nie zmienne leksykalne określić w let. Gdy wywoływana jest funkcja zliczająca, jeśli zmienna globalna nie istnieje, to oczywiście zawiedzie. Zawsze, gdy istnieje taka globalna zmienna, jest ona aktualizowana, co prawdopodobnie nie jest zamierzone i może być trudnym do wykrycia błędem, ponieważ funkcja może działać prawidłowo.

Bajt kompilator nie dać ostrzeżenie, jeśli używasz defun w ten sposób i zapewne sprawa zostanie skierowana w jakiejś przyszłej wersji Emacsa, ale do tej pory następujące makro może być stosowany:

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

Jeśli zdefiniujemy licząc następująco:

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

będzie działać zgodnie z oczekiwaniami i zaktualizować leksykalnie związaną zmienną liczyć przy każdym wywołaniu, zwracając nową wartość.

zastrzeżenie: makro nie będzie działać prawidłowo, jeśli starają się defun ** funkcja o tej samej nazwie, jako jednego z leksykalnie związanych zmiennych. Tzn czy można zrobić coś takiego:

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

Nie mogę sobie wyobrazić, że ktoś rzeczywiście robi, ale było warto wspomnieć.

Uwaga: nazwałem makro defun **tak aby nie kolidować z makro defun * w cl pakietu, jednak nie zależy w żaden sposób, że pakiet.

+0

Czy potrafisz podać pełny przykład? Wydaje się, że kod (let ((counter 0)) (defun ** counting() (licznik setq (licznik 1+))) nie działają zgodnie z oczekiwaniami. – toolchainX

+1

Ograniczenie polegające na zdefiniowaniu zamknięć z 'defun' zostało zniesione w Emacs-24.3 (gdzie' defun' jest teraz definiowany jako makro, a nie jako specjalny formularz). Więc od 24,3 'defun' działa jak twoje' defun ** 'makro (tho bez zepsutego' (defvar, name nil) 'i naprawia różne inne drobne błędy, takie jak użycie' fset' zamiast 'defalias' i obsługa tak zwane "dynamiczne docstrukcje", które wymagały zmian w bajtkompilatorze). – Stefan

Powiązane problemy