Jak wiedzieć, kiedy lub kiedy nie używać pojedynczego cudzysłowu przed nazwami zmiennych?

31

Mam poniżej:

(setq some-variable "less")

Jestem zdezorientowany, dlaczego muszę używać pojedynczego cytatu z, boundpale nie z bound-and-true-p.

Przykład 1:

(when (boundp 'some-variable) 
   (message "some-variable is %s" some-variable))

Wynik:

„jakaś zmienna jest mniejsza”

Przykład 2a:

(when (bound-and-true-p some-variable) ;; Note that using single-quote causes error
   (message "some-variable is %s" some-variable))

Wynik:

„jakaś zmienna jest mniejsza”

Przykład 2b:

(when (bound-and-true-p 'some-variable) ;; Note that using single-quote causes error
   (message "some-variable is %s" some-variable))

Wynik:

i: Nieprawidłowy typ argumentu: symbolp, (cytuj zmienną)

Kaushal Modi
źródło
5
Warto wspomnieć, że to setqoznacza set quoted, i pierwotnie było makrem, które się rozszerzyło (set 'some-variable "less"). Ogólnie rzecz biorąc, Elisp nie jest strasznie konsekwentny w argumentach cytowanych i niecytowanych, ale każda funkcja (nie makro), która musi wchodzić w interakcje ze zmienną zamiast wartości, przyjmuje cytowany argument ( setqjest to główny wyjątek).
shosti
2
FWIW, bound-and-true-pto głupie makro. A raczej jego nazwa jest głupia. 99,99% czasu, kiedy chcesz to (and (boundp 'FOO) FOO)zrobić, aby użyć wartościFOO . Nie robisz tego tylko po to, aby uzyskać wartość prawdy. (1) Makro nie jest potrzebne - zastępowany kod jest trywialny i mały. (2) Nazwa wprowadza w błąd - chodzi o wartość zmiennej, a nie tylko o sprawdzenie, czy wartość zmiennej jest nil.
Drew

Odpowiedzi:

24

Krótka odpowiedź

Jeśli próbujesz użyć samej zmiennej, użyj 'some-variable. Jeśli próbujesz użyć wartości przechowywanej w zmiennej, użyj some-variable.

  • boundp używa symbolu, więc będzie patrzył na wszystko, co może być powiązane, w tym funkcje. Dba tylko o to, czy istnieje odpowiedni symbol, a nie o wartość.
  • bound-and-truep używa var i zwraca wartość. W takim przypadku musisz podać wartość symbolu dla funkcji. Jeśli żaden symbol var nie jest związany lub wartość jest równa zero, to zwróci zero.

Wyjaśnienie

Definicja instrukcji znajduje się w instrukcji .

'i (quote ...)oba spełniają ten sam cel w emacs-lisp.

Ma to na celu przekazanie nieocenionej formy do otaczającego środowiska, a nie jej ocenę.

W twoim przykładzie załóżmy, że mieliśmy następującą wyżej

(setq some-variable "less") ;; Rather than just 't for clarity

Następnie ocena przebiega następująco:

(when (boundp 'some-variable) 
   (message "some-variable is %s" some-variable))
;; ==> (boundp 'some-variable) ; 't
;; ==> some-variable is "less"

Podczas gdy bez cytatu:

(when (boundp some-variable) ;; Note that using single-quote causes error
   (message "some-variable is %s" some-variable))
;; ==> (boundp "less") ; "less" is not a variable. -> Error

Lisp ocenia formularze, gdy są osiągane, poprzez cytowanie formularza, który uniemożliwia ocenę, tak aby przekazywana była rzeczywista zmienna (lub lista lub nazwa funkcji).

Jonathan Leech-Pepin
źródło
Dzięki! Twoja odpowiedź sprawiła, że ​​wskoczyłem do źródeł obu i pomógł mi znaleźć rozwiązanie. Zaktualizowałem również moje pytanie, podając jasne przykłady.
Kaushal Modi
6
Cieszę się, że pomogło to operacji, ale tak naprawdę nie odpowiada na pytanie (w sposób przydatny dla innych osób mających to samo pytanie). Wyjaśniasz tylko, że symbole muszą być cytowane lub są oceniane. Nie wyjaśniasz, dlaczego tak nie jest, bound-and-truepa pytanie brzmiało: dlaczego muszę cytować podczas korzystania, boundpale nie podczas korzystania bound-and-truep.
tarsjusz
@tarsius W rzeczywistości uwzględniam rozróżnienie między boundpwymaganiem symbolu (nieoceniony) a bound-and-trueppotrzebą wartości zmiennej w punktorach (edytowanej po wstępnej odpowiedzi)
Jonathan Leech-Pepin
Myślę, że należy wspomnieć, dlaczego tak jest (makra mogą zdecydować się nie oceniać), zamiast po prostu wspomnieć, że tak napisano w dokumencie. Myślę, że to bardzo dobre pytanie, a boundp vs. bound-and-true-p to tylko przykład. To, co tak naprawdę sprowadza się do pytania, to chęć poznania zasad oceny.
tarsjusz
@tarsius Czy reguły oceny nie są wyjaśnione w tej odpowiedzi? Przynajmniej podstawowe z tylko cytatem ... Twoja odpowiedź jest zdecydowanie bardziej kompletna, ale wykracza daleko poza temat, prawda?
T. Verron,
16

Symbol, który znajduje się w pozycji niefunkcjonalnej, jest traktowany jako nazwa zmiennej. In (function variable) functionznajduje się w pozycji funkcji (po nawiasie otwierającym) i variablenie jest. Chyba, że ​​zmienne podane wprost są zastępowane ich wartościami.

Jeśli miałbyś pisać (boundp my-variable), oznaczałoby to „to symbol, który jest przechowywany w wartości zmiennej my-variablezwiązanej jako zmienna”, a nie „to symbol my-variablezwiązany jako zmienna.

Dlaczego więc bound-and-truepzachowuje się inaczej?

Jest to makro i nie mają tu zastosowania normalne reguły oceny (funkcji), makra mogą swobodnie decydować, czy i kiedy ich argumenty zostaną ocenione. Makra faktycznie przekształcają argumenty i zwracają wynik jako listę, która jest następnie analizowana. Transformacja i ocena końcowa odbywają się w różnych momentach, zwanych czasem ekspansji makr i czasem oceny.

Oto bound-and-true-pjak wygląda definicja :

(defmacro bound-and-true-p (var)
  "Return the value of symbol VAR if it is bound, else nil."
  `(and (boundp (quote ,var)) ,var))

Używa to makr czytnika, które różnią się od makr lisp (więcej na ten temat poniżej). Aby to nie komplikować, nie używajmy żadnych makr czytników:

(defmacro bound-and-true-p (var)
  "Return the value of symbol VAR if it is bound, else nil."
  (list 'and (list 'boundp (list 'quote var)) var))

Jeśli napiszesz

(bound-and-true-p my-variable)

to jest najpierw „przetłumaczone” na

(and (boundp 'my-variable) my-variable)

a następnie jest to zwracane, niljeśli my-variablenie jest, boundplub wartość my-variable(która oczywiście może być nil).


Być może zauważyłeś, że rozszerzenie nie było

(and (boundp (quote my-variable)) my-variable)

jak mogliśmy się spodziewać. quoteto specjalna forma, a nie makro lub funkcja. Podobnie jak makra, specjalne formularze mogą zrobić wszystko z ich argumentami. Ta szczególna specjalna forma po prostu zwraca swój argument, tutaj symbol, zamiast zmiennej wartości symbolu. To właściwie jedyny cel tej specjalnej formy: zapobieganie ewaluacji! Makra nie można zrobić na własną rękę, trzeba użyć quote, aby to zrobić.

Więc o co chodzi '? Jest to makro czytnika , które, jak wspomniano powyżej, nie jest tym samym, co makro lisp . Podczas gdy makra są używane do przekształcania kodu / danych, makra czytające są używane wcześniej podczas czytania tekstu w celu przekształcenia tego tekstu w kod / dane.

'something

jest krótką formą dla

(quote something)

`w rzeczywistej definicji bound-and-true-prównież używane jest makro czytnika. Jeśli cytuje on symbol taki jak w `symbolnim, jest on równoważny 'symbol, ale kiedy jest używany do cytowania listy, ponieważ `(foo bar ,baz)zachowuje się inaczej w tym, że ,oceniane są formularze z prefiksem .

`(constant ,variable)

jest równa

(list (quote constant) variable))

Powinno to odpowiedzieć na pytanie, dlaczego niecytowane symbole są czasami oceniane (zastępowane ich wartościami), a czasem nie; makra mogą służyć quotedo zapobiegania ocenie symboli.

Ale dlaczego bound-and-true-pmakro boundpnie jest? Musimy być w stanie ustalić, czy dowolne symbole, które nie są znane do czasu wykonania, są powiązane jako symbole. Nie byłoby to możliwe, gdyby boundpargument był cytowany automatycznie.

bound-and-true-psłuży do ustalenia, czy znana zmienna jest zdefiniowana, a jeśli tak, użyj jej wartości. Jest to przydatne, jeśli biblioteka ma opcjonalną zależność od biblioteki innej firmy, jak w:

(defun foo-get-value ()
  (or (bound-and-true-p bar-value)
      ;; we have to calculate the value ourselves
      (our own inefficient or otherwise undesirable variant)))

bound-and-true-pmożna zdefiniować jako funkcję i wymagać cytowania argumentu, ale ponieważ jest on przeznaczony do przypadków, w których wiadomo z góry, jaką zmienną, na której Ci zależy, makro użyto, aby uniknąć konieczności wpisywania '.

Tarsjusz
źródło
Niezwykle dobrze odpowiedział.
Charles Ritchie,
2

Z kodu źródłowego boundp:

DEFUN ("boundp", Fboundp, Sboundp, 1, 1, 0,
      doc: /* Return t if SYMBOL's value is not void.
Note that if `lexical-binding' is in effect, this refers to the
global value outside of any lexical scope.  */)

boundpoczekuje symboljako danych wejściowych. 'some-variablejest symbolem zmiennej some-variable.

Z kodu źródłowego bound-and-true-p:

(defmacro bound-and-true-p (var)
  "Return the value of symbol VAR if it is bound, else nil."
  `(and (boundp (quote ,var)) ,var))

bound-and-true-poczekuje variablejako danych wejściowych.

Wewnątrz bound-and-true-pmakra otrzymuje symbol poprzez wykonanie (quote ,var). Więc jeśli wejście jest some-variable, (quote ,var)spowoduje 'some-variable.

Ale kiedy dać wejście 'some-variabledo bound-and-true-p, pojawia się błąd: and: Wrong type argument: symbolp, (quote some-variable)bo Makro nie spodziewa się symbol ( 'some-variable) na wejściu.

Kaushal Modi
źródło
Tylko heads-up. ''symbol ma sens. Oznacza to (quote (quote symbol)). Przyczyną błędu jest to, że nie jest to prawidłowy argument boundp.
Malabarba
@Malabarba Thanks. Dokonałem korekty.
Kaushal Modi