使用变量定义可选参数

时间:2015-10-26 18:35:35

标签: emacs elisp org-mode

我不知道我在做什么或术语是什么。我只知道我想使用变量(?)来避免在几个地方重复一个值。但我无法使用该变量(?)的值(?)将其传递给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)

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

2 个答案:

答案 0 :(得分:4)

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

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

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

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

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

(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评估一些表达式。

Backquotes有点像Python的字符串插值,除了它们适用于整个lisp语法而不仅仅是双引号字符串。

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

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

特别注意`((“my-notes”:base-directory,my-base-directory))不起作用;你的一条评论表明你可能尝试过这样的事情。这种逗号的使用对来自其他语言的程序员来说确实很陌生!你不是一个人认为这是一种非常奇怪的写东西的方式,但过了一段时间它确实开始有意义。将逗号视为一种颠倒的反引号是有帮助的。

答案 1 :(得分:3)

示例:

反引号列表:

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
  ;  ...
    )))