将相同的值分配给多个变量?


12

有时我需要为多个变量设置相同的值。

在Python中,我可以

f_loc1 = f_loc2 = "/foo/bar"

但是在elisp中,我正在写

(setq f_loc1 "/foo/bar" f_loc2 "/foo/bar")

我想知道是否有一种方法可以"/foo/bar"只使用一次?


1
嗨,希拉尔,您能选择@tarsius的答案作为接受的答案吗?我的答案演示了可以为您提供所需解决方案的解决方案,但是正如我所说的那样,它是“一种运动”,可以解决这一可能是人为的挑战,但在实践中却没有什么用处。tarsuis在他的答案中花了很多精力来证明这一显而易见的考虑,因此我认为他的答案应该是“正确的”,所以每个人都再次感到高兴。
Mark Karpov

1
我认为需要为多个变量分配相同的值表示设计缺陷应得到修复,而不是寻找语法糖来掩盖:)
lunaryorn

1
@lunaryorn如果想在开始时将source&设置target为相同的路径,而我target以后可能会更改,那么如何设置它们?
ChillarAnand 2015年

显示更多代码,因此我们可以告诉您用例“变量”的含义;即,您要设置哪种变量。看到我的评论@JordonBiondo的答案。
提请

Answers:


21

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-letdash.el库。这确实使那些在我的代码中首次接触到它的人更加困难,但是克服这种理解上的困难是值得的,因为它被使用了不止几次,并且至少在我看来,它使代码更具可读性。 。

有人可能会争辩说,同样的道理也适用于setq-every,但是我认为这个特定的宏被多次使用的可能性很小。一个好的经验法则是,当宏的定义(包括doc-string)添加的代码多于使用该宏所删除的代码时,则该宏很可能是过大的(尽管不一定是正确的)。


1
您设置一个变种后,setq你可以参考它的新的价值,所以你可以使用这个怪物过:(setq a 10 b a c a d a e a)这台ABCD和E 10
乔丹比翁

1
@JordonBiondo,是的,但是我认为OP的目的是减少他代码中的重复:-)
Mark Karpov 2015年

3
我想给您+10的警告以防宏滥用!
lunaryorn 2015年

刚刚创建了有关宏最佳实践的任务。emacs.stackexchange.com/questions/21015。希望@tarsius和其他人能给个很好的答案。
Yasushi Shoji

13

宏可以做你想要的

作为一种练习:

(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在宏扩展时求值。

最后@使用setqs 从列表中删除外部括号,因此我们得到:

(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))))

问题解决了。


1
如果您可以添加一些解释说明此方法的工作原理以及此代码的作用的细节,那就太好了,以便下次人们可以自己编写这样的内容:)
clemera

您不想value在宏扩展时进行评估。
tarsius

@tarsius,我不会:(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会有所帮助。
Mark Karpov 2015年

1
mmt-once-only可以使用代替(这需要外部dep)macroexp-let2
YoungFrog 2015年

2
啊。这个问题迫切要求使用带有的迭代set,而不是包装setq在宏中。或仅setq与多个args一起使用,包括args的重复。
德鲁

7

由于您可以使用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))

2
但是,这不适用于词法绑定变量
npostavs

@npostavs:是的,但这可能是OP想要/需要的。如果他不使用词法变量,那肯定是要走的路。您说对了,但是“变量”是模棱两可的,因此总的来说,这个问题可能意味着任何类型的变量。IMO并没有很好地说明实际用例。
德鲁
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.