有时我需要为多个变量设置相同的值。
在Python中,我可以
f_loc1 = f_loc2 = "/foo/bar"
但是在elisp中,我正在写
(setq f_loc1 "/foo/bar" f_loc2 "/foo/bar")
我想知道是否有一种方法可以"/foo/bar"
只使用一次?
source
&设置target
为相同的路径,而我target
以后可能会更改,那么如何设置它们?
有时我需要为多个变量设置相同的值。
在Python中,我可以
f_loc1 = f_loc2 = "/foo/bar"
但是在elisp中,我正在写
(setq f_loc1 "/foo/bar" f_loc2 "/foo/bar")
我想知道是否有一种方法可以"/foo/bar"
只使用一次?
source
&设置target
为相同的路径,而我target
以后可能会更改,那么如何设置它们?
Answers:
setq
返回值,因此您可以:
(setq f-loc1 (setq f-loc2 "/foo/bar"))
如果您不想依赖于此,请使用:
(setq f-loc1 "/foo/bar" f-loc2 f-loc1)
我个人会避免使用后者,而是这样写:
(setq f-loc1 "/foo/bar"
f-loc2 f-loc1)
甚至
(setq f-loc1 "/foo/bar")
(setq f-loc2 f-loc1)
而且,第一种方法只会在真正强调目的的特殊情况下使用,或者当替代方法是第二种方法时使用progn
。
如果以上使您满意,您可以在这里停止阅读。但是我认为这是一个很好的机会,警告您和其他人不要滥用宏。
最后,虽然很想编写一个宏,例如setq-every
“因为这是轻而易举的事,所以我们可以做到”,但我强烈建议您不要这样做。通过这样做,您获得的收益很少:
(setq-every value a b)
vs
(setq a (setq b value))
但是与此同时,您会使其他读者更难阅读代码并确定会发生什么。setq-every
可能以各种方式实现,其他用户(包括您的未来)仅通过查看使用它的代码就无法知道作者选择了哪一个。[我最初在这里举了一些例子,但有人认为这些例子很讽刺和a。
每次查看时setq-every
,您都可以应付这种精神上的负担,也可以只剩下一个额外的角色。(至少在只需要设置两个变量的情况下,但是我不记得我曾经必须一次将两个以上的变量设置为相同的值。如果必须这样做,那么可能还有其他问题与您的代码。)
另一方面,如果您真的要多次使用该宏,则可能需要这样做。比如我使用像宏--when-let
从dash.el
库。这确实使那些在我的代码中首次接触到它的人更加困难,但是克服这种理解上的困难是值得的,因为它被使用了不止几次,并且至少在我看来,它使代码更具可读性。 。
有人可能会争辩说,同样的道理也适用于setq-every
,但是我认为这个特定的宏被多次使用的可能性很小。一个好的经验法则是,当宏的定义(包括doc-string)添加的代码多于使用该宏所删除的代码时,则该宏很可能是过大的(尽管不一定是正确的)。
setq
你可以参考它的新的价值,所以你可以使用这个怪物过:(setq a 10 b a c a d a e a)
这台ABCD和E 10
作为一种练习:
(defmacro setq-every (value &rest vars)
"Set every variable from VARS to value VALUE."
`(progn ,@(mapcar (lambda (x) (list 'setq x value)) vars)))
现在尝试一下:
(setq-every "/foo/bar" f-loc1 f-loc2)
由于人们很好奇它的工作原理(根据评论),这里有一个解释。要真正学习如何编写宏,请选择一本不错的Common Lisp书(是的,Common Lisp,您可以在Emacs Lisp中做同样的事情,但是Common Lisp功能更强大,并且有更好的书,恕我直言)。
宏对原始代码进行操作。宏不评估其参数(与函数不同)。因此,我们在这里未进行评估value
和的集合vars
,对于我们的宏来说,它们只是符号。
progn
将几种setq
形式组合为一个。这东西:
(mapcar (lambda (x) (list 'setq x value)) vars)
只需生成一个setq
表单列表,使用OP的示例,它将是:
((setq f-loc1 "/foo/bar") (setq f-loc2 "/foo/bar"))
您会看到,该表单位于反引号表单的内部,并以逗号作为前缀
,
。在反引号形式内,所有内容通常都被引用,但,
会暂时“打开”求值,因此整体mapcar
在宏扩展时求值。
最后@
使用setq
s 从列表中删除外部括号,因此我们得到:
(progn
(setq f-loc1 "/foo/bar")
(setq f-loc2 "/foo/bar"))
宏可以任意转换您的源代码,这不是很好吗?
这是一个小警告,第一个参数将被评估几次,因为此宏实际上扩展为以下内容:
(progn
(setq f-loc1 "/foo/bar")
(setq f-loc2 "/foo/bar"))
您会看到,如果这里有变量或字符串,就可以了,但是如果您编写如下代码:
(setq-every (my-function-with-side-effects) f-loc1 f-loc2)
然后,您的函数将被多次调用。这可能是不希望的。以下是在的帮助下once-only
(通过
MMT包提供)进行修复的方法:
(defmacro setq-every (value &rest vars)
"Set every variable from VARS to value VALUE.
VALUE is only evaluated once."
(mmt-once-only (value)
`(progn ,@(mapcar (lambda (x) (list 'setq x value)) vars))))
问题解决了。
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
可以使用代替(这需要外部dep)macroexp-let2
。
set
,而不是包装setq
在宏中。或仅setq
与多个args一起使用,包括args的重复。
由于您可以使用Lisp来使用和操作符号,因此您可以简单地遍历符号列表并使用set
。
(dolist (var '(foo bar baz)) (set var 10))
(mapc (lambda (var) (set var 10)) '(foo bar baz))
(loop for var in '(foo bar baz) do (set var 11))
(--each '(foo bar baz) (set it 10))