【问题标题】:How to pass bound symbols to functions in elisp?如何将绑定符号传递给elisp中的函数?
【发布时间】:2022-08-20 06:55:41
【问题描述】:

我正在尝试执行以下操作:将一个从某些用户输入中获取值的函数与使用它的函数分开。

我最初尝试了以下代码来进行概念验证(有效):

(defun initialiser (bindings)
  (cl-loop for (var) in bindings do
           (set var (read-from-minibuffer \"Input value: \"))))

我已经测试过:

(let ((name))
  (initialiser \'((name)))
  (message \"name is %S\" name))

这个想法是将绑定传递给处理((name \"Name\") (address \"Post address\") (code \"County code\"))或类似形式的输入的函数,并在那里分配输入。

经过上面的测试,我想出了以下宏来做事情:

(defmacro initialise-and-execute (bindings initialiser &rest callback-actions)
  (let ((unwrapped-bindings (map \'list (lambda (binding) (car binding)) bindings)))
    `(let ,unwrapped-bindings
       (,initialiser (quote ,bindings)
                     (lambda () ,@callback-actions)))))

但是,在“真实”场景中,分配必须在回调中发生,例如:

(defun initialiser(bindings)
  (cl-loop for (var) in bindings collect
           (lambda () (set var (read-from-minibuffer \"Input value: \")))
           into callbacks
           return callbacks))

这行不通。我用来测试的代码是:

(defvar callbacks nil)
(let ((name))
  (setq callbacks (initialiser \'((name)))))
(funcall (car callbacks))

编辑:通过以下方式更改代码:

(defmacro initialise-and-execute (bindings initialiser &rest callback-actions)
  (let ((unwrapped-bindings (map \'list (lambda (binding) (car binding)) bindings)))
    `(lexical-let ,unwrapped-bindings
       (,initialiser
        (quote ,(map \'list
                     (lambda (binding) (list (cadr binding)
                                             `(lambda (val) (setq ,(car binding) val))))
                     bindings))
        (lambda () ,@callback-actions)))))

它必须做什么:生成多个共享相同词法环境的 lambda - 一个使用捕获的变量,其余的修改它们。

然而,遗憾的是,我得到的是别的东西。 callback-actions 中使用的符号不会解析为值集。

为了完整起见,这是我测试它的方法:

(defun init-values (bindings callback)
  (loop for (desc setter) in bindings
        for idx = 0 then (incf idx)
        do (print (format \"Setting %s\" desc))
        (funcall setter idx))
  (funcall callback))

(initialise-and-execute
                 ((name \"Name\")
                  (surname \"Surname\"))
               init-values
               (message \"name is %S\" name))

这里,lambdas 不是在loop 中生成的,而词法绑定上下文中的值是用setq 分配的。

  • 这个比较不清楚。简单地传递符号应该可以轻松工作。您能否提供一个没有复杂宏的minimal reproducible example,只提供一些绑定但未定义的符号,并演示(set symbol value) 之类的东西如何不像您预期​​的那样工作?但是,我猜你真的是在叫错树。
  • 添加了一个可复制的示例
  • 如果您想将符号和值之间的某种关联传递给某些东西,那么传递 alist 或哈希表或类似的东西几乎总是更好。 set 几乎从来都不是您想要做的。

标签: macros lisp elisp


【解决方案1】:

要设置函数值,请使用fset,而不是set

(defun initialiser(bindings)
  (cl-loop for (var) in bindings collect
           (lambda () (fset var (read-from-minibuffer "Input value: ")))
           into callbacks
           return callbacks))

【讨论】:

    【解决方案2】:

    可能存在的问题:

    • 在循环中,VAR 可能在每次迭代中分配,而不是绑定(在 Common Lisp 中)。所以你的回调设置了所有相同的 VAR 值。

    • 在一个词法绑定的 Lisp(GNU Emacs Lisp 中的新功能)中不能用 SET 设置变量值

    例子:

    ELISP> (mapcar #'funcall
                   (cl-loop for i in '(1 2 3 4)
                            collect (lambda () i)))
    (4 4 4 4)
    

    所有闭包都具有相同的 i 值

    ELISP> (let ((a 'foobar))
             (set 'a 42)
             a)
    foobar
    

    SET 对局部变量没有影响

    【讨论】:

    • 这两件事似乎都是我遇到的问题。然而,试图改变它们似乎并没有帮助。我会将更改后的代码添加到问题中
    【解决方案3】:

    经过一些实验,我已经能够做到。关键是意识到lexical-let 显然对如何放置 lambda 以使它们具有相同的闭包上下文有一些限制。在我设法生成一组引用相同变量的闭包之后,剩下的就很容易了。

    这是最终代码:

    (defmacro make-lamba-set (bindings &rest callback-actions)
      "Make set of closures with shared context
    BINDINGS are a list of form (SYMBOL NAME), where SYMBOL is not defined yet
    CALLBACK-ACTIONS are forms that use symbols from bindings
    Return created captures a as a list of forms:
    (do-callback-actions (set-symbol NAME) ...)"
      (let ((unwrapped-bindings (map 'list (lambda (binding) (car binding)) bindings)))
        `(lexical-let ,unwrapped-bindings
           (list (lambda () ,@callback-actions)
                 ,@(map 'list
                        (lambda (binding) `(list
                                            (lambda (val) (setq ,(car binding) val))
                                            ,(cadr binding)))
                        bindings)))))
    
    (defmacro initialise-and-execute (bindings initialiser &rest callback-actions)
      "BINGINGS have form of ((BINDING-SYMBOL PROMPT) ...)
       INITIALISER somehow assigns values to all of BINDING-SYMBOL
       then it calls CALLBACK-ACTIONS with values of BINDING-SYMBOL set"
      `(let ((closure-parts (make-lamba-set ,bindings ,@callback-actions)))
        (,initialiser (cdr closure-parts)
                      (car closure-parts))))
    

    这是我测试它的方法:

    (defun init-values (bindings callback)
      (loop for (setter desc) in bindings
            for idx = 0 then (incf idx)
            do (message "Setting %s" desc)
               (funcall setter idx))
      (funcall callback))
    
    (initialise-and-execute ((name "Name") (surname "Surname")) init-values (insert (format "Name is %S and surname is %S" name surname)))
    

    这给了我预期的输出:

    名字是0,姓是1

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-09-17
      • 2018-06-30
      • 2019-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多