Czy można dołączyć wygenerowany ciąg doc do lambda?

10

Dokumenty Emacsa mówią, że gdy łańcuch dokumentu jest wstawiany do środka lambdalub defunjest „przechowywany bezpośrednio w obiekcie funkcji”. Możemy jednak zmienić dokumenty nazwanych funkcji w następujący sposób:

(put 'my-function-name 'function-documentation "Blah.")

Ale ta sama sztuczka nie działa z lambdami. Czy istnieje sposób na dodanie dokumentacji do lambda? Lub jakoś dynamicznie generować literał ciąg-doc?

Aby to wyjaśnić, wyobraź sobie następującą sytuację:

(let ((foo 1)
      (bar 2))
  (lambda ()
    (+ foo bar)))

Chciałbym, aby lambda miała ciąg dokumentów, który wymienia wartości fooi bar.

Mark Karpov
źródło

Odpowiedzi:

12

Cóż, lambdas mogą mieć regularne dokumenty, tak jak każda inna definicja funkcji:

(lambda ()
   "I'm a docstring!"
   (+ foo bar))

Więc możesz użyć:

(let ((foo 1)
      (bar 2))
  `(lambda ()
     ,(format "Function which sums foo=%s and bar=%s" foo bar)
     (+ foo bar)))

Dlaczego chcesz mieć dokumentację dotyczącą funkcji anonimowej, to kolejne pytanie, które może wpłynąć na twoje podejście.

Na przykład, jeśli planujesz powiązać go z kluczem i chcesz C-h kwyświetlić tę pomoc, możesz użyć tego podejścia, ale oczywiście pomoc nadal będzie również wyświetlać sam obiekt funkcji (w tym dokumentacja), co nie jest takie świetny; Niemniej jednak można to zrobić i będzie (również) zobaczyć wersję ładnie sformatowany:

(global-set-key
 (kbd "C-c a")
 (let ((foo 1)
       (bar 2))
   `(lambda ()
      ,(format "Function which sums foo=%s and bar=%s" foo bar)
      (interactive)
      (+ foo bar))))

Możesz jednak użyć symbolu. Możesz sparować anonimową funkcję z niewewnętrznym symbolem i nie martw się, że będzie ona kolidować z innymi symbolami o tej samej nazwie. Dzięki temu pomoc jest czystsza, ponieważ wyświetli nazwę symbolu, a nie obiekt funkcji. W tym przypadku mamy opcję przekazania docstring defaliaszamiast osadzania go w formie lambda.

(global-set-key
 (kbd "C-c a")
 (let ((foo 1)
       (bar 2))
   (defalias (make-symbol "a-foo-bar-function")
     (lambda ()
       (interactive)
       (+ foo bar))
     (format "Function which sums foo=%s and bar=%s" foo bar))))

lub (i to jest bardzo to samo) możesz przechwycić nienaruszony symbol i ustawić właściwość symbol bezpośrednio, zgodnie z oryginalnym kodem:

(global-set-key
 (kbd "C-c a")
 (let ((foo 1)
       (bar 2)
       (sym (make-symbol "a-foo-bar-function")))
   (put sym 'function-documentation
        (format "Function which sums foo=%s and bar=%s" foo bar))
   (defalias sym
     (lambda ()
       (interactive)
       (+ foo bar)))))

Na marginesie należy pamiętać, że ta funkcja jest tylko będzie zsumowanie wartości let-wiązany dla fooi barjeśli używasz lexical-binding: tdo swojej biblioteki. Jeśli foo i pasek są dynamicznie powiązane, wygenerowane przeze mnie dokumenty prawdopodobnie nie będą dokładne w czasie wykonywania. Możemy jednak faktycznie zaspokoić tę sytuację za pomocą dynamicznych dokumentów . Węzeł informacyjny (elisp) Accessing Documentationmówi o documentation-property:

Jeśli wartość właściwości nie jest równa „zero”, nie jest łańcuchem i nie odnosi się do tekstu w pliku, to jest obliczana jako wyrażenie Lisp w celu uzyskania łańcucha.

Tak więc w przypadku dowolnego podejścia opartego na symbolach możemy zacytować formularz dokumentacji, aby móc go ocenić w czasie połączenia:

(defalias (make-symbol "a-foo-bar-function")
   (lambda ()
     (interactive)
     (+ foo bar))
   '(format "Function which sums foo=%s and bar=%s" foo bar))
phils
źródło
13

W Emacs-25 wprowadzono nową funkcję właśnie w tym celu:

(let ((foo 1)
      (bar 2))
  (lambda ()
    (:documentation (format "Return the sum of %d and %d." foo bar))
    (+ foo bar)))
Stefan
źródło