使用变量定义可选参数

Use a variable to define an optional argument

我不知道我在做什么或术语是什么。我只知道我想使用一个变量(?)来避免在几个地方重复一个值。但是我无法使用该变量 (?) 的值 (?) 将其传递给 emacs lisp 中的可选(关键字?)参数。

使用文字值有效:

(setq
 org-publish-project-alist
 '(
   ("my-notes"
    :base-directory "projects/notes"
    :base-extension "org"
    :publishing-directory "html"
    ...
    )
   ))

使用变量无效:

(setq my-base-directory "~/projects/notes")
(setq my-publising-directory "html")

(setq
 org-publish-project-alist
 '(
   ("my-notes"
    :base-directory my-base-directory
    :base-extension "org"
    :publishing-directory my-publishing-directory
    ...
    )
   ))

我收到一个错误:

byte-code: Wrong type argument: stringp, my-base-directory

这也不起作用:

(let (
      (my-base-directory "~/projects/notes")
      (my-publising-directory "html")
      )
  (setq
   org-publish-project-alist
   '(
     ("my-notes"
      :base-directory my-base-directory
      :base-extension "org"
      :publishing-directory my-publishing-directory
      ...
      )
     )))

我想知道为什么在 emacs 中访问变量如此困难?这些变量被定义了(我可以打印它们),但是它们不能被可选参数访问(那些前面有冒号的东西叫做可选参数或关键字参数吗?)

我尝试了无数其他变体,包括 (quote ...) 和使用引号 ':

(setq
 org-publish-project-alist
 '(
   ("my-notes"
    :base-directory 'my-base-directory
    :base-extension "org"
    :publishing-directory 'my-publishing-directory
    ...
    )
   ))

在这种情况下,我得到:

byte-code: Wrong type argument: stringp, (quote my-base-directory)

我只需要使用变量来设置可选参数。我怎样才能做到这一点?我是否需要对可选参数使用与正常使用不同的语法?

示例:

反引号列表:

ELISP> (let ((foo "Foo")) `(,foo bar))
("Foo" bar)

函数LIST:

ELISP> (let ((foo "Foo")) (list foo 'bar))
("Foo" bar)

示例:

(setq
 org-publish-project-alist
 `(("my-notes"
    :base-directory ,my-base-directory
    :base-extension "org"
    :publishing-directory ,my-publishing-directory
  ;  ...
    )))

我看到你已经有了一个有效的答案,但在你的评论中表达了一些困惑;我会尝试更彻底地解释它。

当你引用某些东西时(通过使用 (quote foo)'foo),你是在告诉 lisp 解释器不要计算 foo。这通常用于输入数据而不是代码;在这种情况下,您正在创建一个适度复杂的列表结构以用作数据,而不是用作要评估的代码。

您的代码有 '(("my-notes" :base-directory my-base-directory)),这是一个列表的引用列表。内部列表将包含关键字 :base-directory 和符号 my-base-directory。它不包含 my-base-directory 变量的值,因为开头的引号告诉 lisp 不要对它求值。

解决这个问题的两种方法都涉及不引用整个表达式。

第一个也是最简单的选择是使用 list;这是一个函数,它接受任意数量的参数和 returns 包含这些值的列表。例如:

(list 1 2 3) => '(1 2 3)
(let ((x 42)) (list x x)) => '(42 42)

因此,您可以像这样构建代码:

(setq org-publish-project-alist
      (list (list "my-notes" :base-directory my-base-directory
                             :base-extension "org"
                             :publishing-directory my-publishing-directory)))

您会发现必须调用 list 函数两次,结构的每个级别一次。随着结构变得更加复杂,这可能会变得乏味;引用的发明是为了让您免于乏味。

(另请注意,关键字和字符串对其自身求值,因此当它们用作参数而不是引用列表的成员时不需要任何特殊处理。)

另一种选择是使用反引号(或偶尔称为语法引号)而不是正常的 quote。反引号是不寻常的,因为它可以使用逗号运算符撤消:

(setq org-publish-project-alist
      `(("my-notes" :base-directory ,my-base-directory
                    :base-extension "org"
                    :publishing-directory ,my-publishing-directory)))

反引号告诉 lisp 不要计算表达式,就像普通引号一样,但后面的逗号让您改变主意,让 lisp 最终计算一些表达式。

反引号有点像 Python 的字符串插值,不同之处在于它们适用于整个 lisp 语法,而不仅仅是双引号字符串。

此外,您可以在逗号后放置任意 lisp 表达式,就像您可以在引号或反引号后放置任意 lisp 表达式一样:

`(("my-notes" :base-directory ,(concat my-projects-directory "notes")))

请特别注意 `(("my-notes" :base-directory, my-base-directory)) 不起作用;您的评论之一表明您可能尝试过这样的事情。逗号的这种用法对于来自其他语言的程序员来说确实很陌生!并不是只有您认为这是一种非常奇怪的书写方式,但一段时间后它开始变得有意义了。将逗号视为一种倒置的反引号会有所帮助。