Иногда мне нужно установить одно и то же значение для нескольких переменных.
В Python я мог бы сделать
f_loc1 = f_loc2 = "/foo/bar"
Но в элиспе я пишу
(setq f_loc1 "/foo/bar" f_loc2 "/foo/bar")
Мне интересно, есть ли способ достичь этого, используя "/foo/bar"
только один раз?
source
&target
на тот же путь в начале, и я могу изменитьtarget
позже, как их установить?Ответы:
setq
возвращает значение, так что вы можете просто:Если вы не хотите на это полагаться, используйте:
Лично я бы избежал последнего и вместо этого написал бы:
или даже
И самый первый подход, который я использовал бы только в довольно особых обстоятельствах, когда он фактически подчеркивал намерение, или когда альтернативой было бы использовать второй подход или иначе
progn
.Вы можете перестать читать здесь, если вышесказанное делает вас счастливым. Но я думаю, что это хорошая возможность предупредить вас и других не злоупотреблять макросами.
Наконец, хотя соблазнительно написать макрос типа
setq-every
«потому что это шутка, и мы можем», я настоятельно рекомендую не делать этого. Делая это очень мало:Но в то же время другим читателям становится намного труднее читать код и быть уверенным, что произойдет.
setq-every
могут быть реализованы различными способами, и другие пользователи (включая будущее, которое вы) не можете знать, какой из них выберет автор, просто взглянув на код, который его использует. [Первоначально я привел несколько примеров, но кто-то считал их саркастическими и напыщенными.]Вы можете справляться с этими ментальными накладными расходами каждый раз, когда смотрите
setq-every
или можете просто жить с одним дополнительным персонажем. (По крайней мере, в случае, когда вам нужно установить только две переменные, но я не могу вспомнить, чтобы мне когда-либо приходилось устанавливать более двух переменных одновременно на одно и то же значение. Если вам нужно сделать это, то, вероятно, что-то еще не так с вашим кодом.)С другой стороны, если вы действительно собираетесь использовать этот макрос более полудюжины раз, то дополнительная косвенность может стоить того. Например, я использую макросы, как
--when-let
изdash.el
библиотеки. Это усложняет задачу для тех, кто впервые сталкивается с этим в моем коде, но преодоление этой трудности в понимании того стоит, потому что он используется не очень редко, и, по крайней мере, на мой взгляд, делает код более читабельным. ,Можно утверждать, что то же самое относится и к
setq-every
, но я думаю, что очень маловероятно, что этот конкретный макрос будет использоваться более чем несколько раз. Хорошее эмпирическое правило заключается в том, что когда определение макроса (включая строку документа) добавляет больше кода, чем удаляет использование макроса, тогда макрос, скорее всего, является избыточным (хотя обратное не всегда верно).источник
setq
вы можете ссылаться на ее новое значение, так что вы также можете использовать это чудовище:(setq a 10 b a c a d a e a)
которое устанавливает abcd и e в 10.Макрос, чтобы делать то, что вы хотите
Как своего рода упражнение:
Теперь попробуйте это:
Как это работает
Поскольку людям любопытно, как это работает (согласно комментариям), вот объяснение. Чтобы по-настоящему научиться писать макросы, выберите хорошую книгу по Common Lisp (да, Common Lisp, вы сможете делать то же самое в Emacs Lisp, но Common Lisp немного мощнее и имеет лучшие книги, IMHO).
Макросы работают с необработанным кодом. Макросы не оценивают свои аргументы (в отличие от функций). Итак, мы имеем здесь недооцененные
value
и коллекцииvars
, которые для нашего макроса являются просто символами.progn
группирует несколькоsetq
форм в одну. Эта вещь:Просто генерирует список
setq
форм, на примере OP это будет:Видите ли, форма находится внутри формы обратной кавычки и начинается с запятой
,
. Внутри обратной кавычки все цитируется как обычно, но,
«mapcar
включенная » оценка временно, поэтому все оценивается во время макроразложения.Наконец,
@
удаляет внешнюю скобку из списка с помощьюsetq
s, поэтому мы получаем:Макросы могут произвольно преобразовать ваш исходный код, не правда ли?
Предостережение
Вот небольшая оговорка, первый аргумент будет оцениваться несколько раз, потому что этот макрос существенно расширяется до следующего:
Видите ли, если у вас есть переменная или строка здесь все в порядке, но если вы напишите что-то вроде этого:
Тогда ваша функция будет вызываться более одного раза. Это может быть нежелательно. Вот как это исправить
once-only
(доступно в пакете MMT ):И проблема ушла.
источник
value
во время расширения макроса.(macroexpand '(setq-every user-emacs-directory f-loc1 f-loc2))
->(progn (setq f-loc1 user-emacs-directory) (setq f-loc2 user-emacs-directory))
. Если быvalue
они оценивались во время макроразложения, то это было бы заменено фактической строкой. Вы можете поместить вызов функции с побочными эффектами, вместо этогоvalue
он не будет оцениваться, пока не будет запущен расширенный код, попробуйте.value
как аргумент макроса не оценивается автоматически. Реальная проблема заключается в том, чтоvalue
будет оцениваться несколько раз, что может быть нежелательным,once-only
может помочь здесь.mmt-once-only
(который требует внешнего депо), вы можете использоватьmacroexp-let2
.set
, а не для переносаsetq
в макрос. Или просто используйтеsetq
с несколькими аргументами, включая повторение аргументов.Поскольку вы можете использовать и манипулировать символами в lisp, вы можете просто перебрать список символов и использовать
set
.источник