Czasami muszę ustawić tę samą wartość dla wielu zmiennych.
W Pythonie mogłem
f_loc1 = f_loc2 = "/foo/bar"
Ale w elisp piszę
(setq f_loc1 "/foo/bar" f_loc2 "/foo/bar")
Zastanawiam się, czy istnieje sposób na osiągnięcie tego celu "/foo/bar"
tylko raz?
source
i tętarget
samą ścieżkę na początku, a ja mogę zmienićtarget
później, jak je ustawić?Odpowiedzi:
setq
zwraca wartość, dzięki czemu możesz po prostu:Jeśli nie chcesz na tym polegać, użyj:
Osobiście unikałbym tego drugiego i zamiast tego napisałbym:
lub nawet
I pierwsze podejście zastosowałbym tylko w dość szczególnych okolicznościach, w których faktycznie podkreśla intencję, lub gdy alternatywą byłoby użycie drugiego podejścia, albo inaczej
progn
.Możesz przestać czytać tutaj, jeśli powyższe sprawia ci przyjemność. Ale myślę, że to dobra okazja, aby ostrzec ciebie i innych przed nadużywaniem makr.
Wreszcie, choć kuszące jest pisanie makra typu
setq-every
„ponieważ jest to seplenienie i możemy”, zdecydowanie odradzam to. Zyskujesz bardzo niewiele, robiąc to:Ale jednocześnie znacznie utrudniasz innym czytelnikom czytanie kodu i upewnienie się, co się stanie.
setq-every
może być zaimplementowany na różne sposoby, a inni użytkownicy (w tym przyszli, których jesteś) nie mogą wiedzieć, który autor wybrał, po prostu patrząc na kod, który go używa. [Pierwotnie podałem tutaj kilka przykładów, ale zostały one przez kogoś uznane za sarkastyczne i rant.]Możesz poradzić sobie z tym mentalnym kosztem za każdym razem, gdy na niego patrzysz,
setq-every
lub możesz po prostu żyć z jedną dodatkową postacią. (Przynajmniej w przypadku, gdy musisz ustawić tylko dwie zmienne, ale nie pamiętam, że kiedykolwiek musiałem ustawić więcej niż dwie zmienne na tę samą wartość. Jeśli musisz to zrobić, to prawdopodobnie jest coś innego złego z twoim kodem.)Z drugiej strony, jeśli naprawdę zamierzasz używać tego makra więcej niż może kilkanaście razy, dodatkowa pośrednia może być tego warta. Na przykład używam makr jak
--when-let
zdash.el
biblioteki. Utrudnia to tym, którzy po raz pierwszy napotykają go w moim kodzie, ale warto przezwyciężyć tę trudność ze zrozumieniem, ponieważ jest on używany więcej niż tylko kilka razy, a przynajmniej moim zdaniem sprawia, że kod jest bardziej czytelny .Można argumentować, że to samo dotyczy
setq-every
, ale myślę, że jest bardzo mało prawdopodobne, aby to konkretne makro zostało użyte więcej niż kilka razy. Dobrą zasadą jest to, że gdy definicja makra (w tym ciąg doc) dodaje więcej kodu niż użycie makra usuwa, wówczas makro najprawdopodobniej jest nadmiernie zabójcze (choć sytuacja odwrotna nie musi być prawdziwa).źródło
setq
możesz odnieść się do jego nowej wartości, więc możesz także użyć tej potworności:(setq a 10 b a c a d a e a)
która ustawia abcd i e na 10.Makro do robienia tego, co chcesz
Jako swego rodzaju ćwiczenie:
Teraz spróbuj:
Jak to działa
Ponieważ ludzie są ciekawi, jak to działa (zgodnie z komentarzami), oto wyjaśnienie. Aby naprawdę nauczyć się pisać makra, wybierz dobrą książkę Common Lisp (tak, Common Lisp, będziesz mógł robić te same rzeczy w Emacs Lisp, ale Common Lisp jest nieco potężniejszy i ma lepsze książki, IMHO).
Makra działają na surowym kodzie. Makra nie oceniają swoich argumentów (w przeciwieństwie do funkcji). Mamy więc tutaj nieocenioną
value
i kolekcjęvars
, które dla naszego makra są tylko symbolami.progn
grupuje kilkasetq
formularzy w jeden. Ta rzecz:Po prostu generuje listę
setq
formularzy, na przykładzie OP będzie to:Widzisz, formularz znajduje się w formularzu cudzysłowu i jest poprzedzony przecinkiem
,
. Wewnątrz cudzysłowu wszystko jest cytowane jak zwykle, ale,
ocena „włącza” tymczasowo, więc całośćmapcar
jest oceniana w czasie makroekspresji.Wreszcie
@
usuwa zewnętrzny nawias z listy za pomocąsetq
s, więc otrzymujemy:Makra mogą dowolnie przekształcać kod źródłowy, prawda?
Zastrzeżenie
Oto małe zastrzeżenie, pierwszy argument zostanie oceniony kilka razy, ponieważ makro to zasadniczo rozwija się w następujący sposób:
Widzisz, jeśli masz tutaj zmienną lub ciąg znaków, jest OK, ale jeśli napiszesz coś takiego:
Wtedy twoja funkcja zostanie wywołana więcej niż raz. Może to być niepożądane. Oto jak to naprawić za pomocą
once-only
(dostępnego w pakiecie MMT ):Problem zniknął.
źródło
value
w czasie makroekspansji.(macroexpand '(setq-every user-emacs-directory f-loc1 f-loc2))
->(progn (setq f-loc1 user-emacs-directory) (setq f-loc2 user-emacs-directory))
. Gdybyvalue
zostały ocenione w czasie makroekspresji, zostałby zastąpiony rzeczywistym ciągiem znaków. Możesz umieścić wywołanie funkcji z efektami ubocznymi, zamiast tegovalue
nie będzie ono oceniane, dopóki nie zostanie uruchomiony rozszerzony kod, spróbuj.value
ponieważ argument makra nie jest automatycznie oceniany. Prawdziwy problem polega na tymvalue
, że zostanie to kilkakrotnie ocenione, co może być niepożądane,once-only
może tutaj pomóc.mmt-once-only
(który wymaga zewnętrznego dep), możesz użyćmacroexp-let2
.set
, a nie zawijaniesetq
makra. Lub po prostu użyjsetq
z wieloma argumentami, w tym powtarzaniem argumentów.Ponieważ możesz używać i manipulować symbolami w lisp, możesz po prostu przeglądać listę symboli i używać
set
.źródło