【问题标题】:In elisp, how do I apply backquote to lists read from files在 elisp 中,如何将反引号应用于从文件读取的列表
【发布时间】:2016-03-04 23:02:04
【问题描述】:

我想获取一个大列表(想想 emacs 主题中的面孔)并将其分解为单独文件中的较小列表。我遇到的问题是在我读入列表后将(backquote) 应用到列表中。

这是我用来试验解决方案的代码:

(defvar x 23)

(defun read-from-file (file)
  (with-temp-buffer
    (insert-file-contents file)
    (read (current-buffer))))

;;(defun apply-macro (macro arg-list)
;;  (eval
;;   `(,macro ,@(loop for arg in arg-list
;;                    collect `(quote ,arg)))))

(defvar parts (mapcar 'read-from-file (directory-files "./parts/" "parts/" "\.part$")))

;;(apply-macro 'backquote parts)

parts 

此代码依赖于名为parts/ 的子目录中的“数据”文件。以下是一些示例:

  1. parts/one.part
    `( ("one" "two" "three") ("ten" ,x "twelve") )
    注意:,x 在这个。我想在从文件中读取表达式后对其进行评估。
  2. parts/two.part
    ((“四”“五”“六”)(2 4 6)(9 87 6))
  3. parts/three.part
    ((“七”“八”“九”))

读取“部分”文件没有问题。 (defvar parts (mapcar ... ) 表达式有效。

问题在于,一旦我在 parts 变量中获得了列表,我就无法找到评估 ,x 的方法,因为如果整个列表被反引号而不是从文件中读取,就会出现这种情况。

我已经尝试过this question 中建议的a solution。你可以看到我上面的代码中注释掉了apply-macro 函数。当我运行它时,我得到:

Debugger entered--Lisp error: (wrong-number-of-arguments #[(structure) "\301!A\207" [structure backquote-process] 2 1628852] 3)
  #[(structure) "\301!A\207" [structure backquote-process] 2 1628852]((quote (\` (("one" "two" "three") ("ten" (\, x) "twelve")))) (quote (("seven" "eight" "nine"))) (quote (("four" "five" "six") (2 4 6) (9 87 6))))
  (backquote (quote (\` (("one" "two" "three") ("ten" (\, x) "twelve")))) (quote (("seven" "eight" "nine"))) (quote (("four" "five" "six") (2 4 6) (9 87 6))))
  eval((backquote (quote (\` (("one" "two" "three") ("ten" (\, x) "twelve")))) (quote (("seven" "eight" "nine"))) (quote (("four" "five" "six") (2 4 6) (9 87 6)))))
  apply-macro(backquote ((\` (("one" "two" "three") ("ten" (\, x) "twelve"))) (("seven" "eight" "nine")) (("four" "five" "six") (2 4 6) (9 87 6))))
  eval-region(648 678 t #[257 "\300\242b\210\301\207" [(678) (apply-macro (quote backquote) parts)] 2 "\n\n(fn IGNORE)"])  ; Reading at buffer position 651
  eval-defun-2()
  #[257 "\211\203

【问题讨论】:

    标签: emacs elisp


    【解决方案1】:

    反引号做了一些有趣的事情。在 Emacs lisp 中,读取准引用列表的返回值是以下结构的列表:

    ELISP> (defvar x (car (read-from-string "`(1 2 ,x)")))
    
    ELISP> (car x)
    \`
    
    ELISP> (cdr x)
    ((1 2
        (\, x)))
    
    ELISP> (caddr (cadr x))
    (\, x)
    
    ELISP> (consp (caddr (cadr x)))
    t
    

    因此,如果您打算使用准引用列表,您可能需要自己执行替换。例如,您可以这样做:

    (defun replace-item (item new-item seq)
      (let ((found-item (member item seq)))
        (when found-item
          (setf (car found-item) new-item))
        seq))
    
    
    ELISP> (replace-item '(\, x) 'z (cadr x))
    (1 2 z)
    

    PS。 Common Lisp 对逗号字符做了一些奇怪的事情,在读取相同的列表后,,X 变成了 SB-IMPL::COMMA 类型的对象(在 SBCL 中):它既不是符号,也不是一对。

    PPS。不知何故,这些准引号和逗号被阅读器评估器特殊处理,以至于组合 (eval (read <...>)) 不会产生与内部评估器相同的结果。

    有用的东西

    在玩弄反引号和逗号时,我发现以下内容有效,尽管它有点 hack。

    首先,不要反引号你的结构:它不会造成任何伤害,但它也不会引入任何东西。只需(a b ,c)

    当您读取它时(使用文件中的readread-from-string),它将被转换为:

    ELISP> (setq x (car (read-from-string "(a b ,c)")))
    (a b
       (\, c))
    

    现在,神奇之处:有宏 backquote 进行替换,但它接受 结构:它不评估其参数,因此使其作用于 x必须做到以下几点:

    ELISP> (let ((c 10)) (eval `(backquote ,x)))
    (a b 10)
    

    如您所见,(\, c) 已根据需要替换为 c 的本地绑定。

    购买力平价。人们会期望从字符串 "(a b ,c)"would produce(backquote (a b ,c))` 中读取,但事实并非如此。

    我希望这能提供答案。

    【讨论】:

    • 谢谢。看起来我会为要替换的每个符号进行replace-item 调用。例如如果我在文件中的列表包含 ,x ,y ,那么我需要为每一个调用 replace-item 。我想我可以找到所有需要替换的逗号部分并映射它们或其他东西,但如果有一种方法可以应用反引号会更好。
    • @HenryBone 我想我找到了让 Elisp 做你想做的事的方法:我已经更新了答案。
    • @HenryBone 其实可以更简单:(let ((c 10)) (eval (car (read-from-string "(a b ,c)")))) => (a b 10)`
    • 谢谢@mobiuseng。打勾。 :-)
    猜你喜欢
    • 2011-08-19
    • 2018-11-24
    • 1970-01-01
    • 2020-09-18
    • 1970-01-01
    • 2010-11-14
    • 2022-08-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多