【问题标题】:Mutator elisp functions突变 elisp 函数
【发布时间】:2013-06-29 10:46:47
【问题描述】:

如何定义 mutator elisp 函数?也就是说,如何将参数发送到可以在函数内部修改以在函数外部使用的 elisp 函数(类似于 C++ 中的非 const 引用变量或指针)?例如,假设我有一个函数 foo 定义为

(defun foo (a b c d)
  ;do some stuff to b, c, and d
        .
        .
        .
  )

我不妨这样称呼它,比如说,如下

(defun bar (x)
  (let ((a) (b) (c) (y))
  .
  .
  .
        ;a, b and c are nil at this point
        (foo x a b c)
        (setq y (some-other-function-of a b c x and-other-variables))
  .
  .
  .
)) ... ) 
  y)

我知道我可以将某个函数的所有本地参数放入一个大的旧列表中,评估函数末尾的列表,然后从其他一些列表中获取这些变量设置为该函数的返回值(东西清单),即

(setq return-list (foo read-only-x read-only-y))
(setq v_1 (car return-list))
(setq v_2 (cadr return-list))
            .
            .

但是有没有更好的方法?到目前为止,我在尝试解决这个问题时所做的只是退出函数,其中的变量与传入的方式没有什么不同

至于为什么我希望能够做到这一点,我只是尝试重构一些大型函数 F,使与某些可命名概念 c 相关的所有表达式集合都存在于它们自己的小模块 c_1、c_2、c_3 中。 .. c_n 我可以在 F 中调用我需要在此过程中更新的任何参数。也就是说,我希望 F 看起来像:

(defun F ( ... )
  (let ((a_1) (a_2) ... )
    (c_1 a_1 ... a_m)
    (c_2 a_h ... a_i)
     .
     .
     .
    (c_n a_j ... a_k)
     .
     .
     .
 ))...))

【问题讨论】:

  • 所以你从一个写得很糟糕的文件开始(例如,大多数 vars 以 let 开头,没有给它们一个值),然后想通过移动让它变得更糟其他地方的副作用,所以他们更难追查?您最好在列表中返回新值(如您所建议的那样)。您可以使用pcase-let 更方便地分解return-list,如(pcase-let (((,v_1 ,v_2) (foo read-only-x read-only-y))) ..)`。
  • 我同意你关于未分配的 let 变量没有价值的观点(尽管在实际代码中,如果有机会,我将在声明中定义它们),至于副作用,我不太确定我同意。使用一致的变量命名约定(即 i-var 表示只读,o-var 表示只写,io-var 表示读写,等等......)然后通过检查,我知道我打算使用哪些变量到副作用。而且,无论我是从函数返回值还是通过副作用赋值,我仍然必须全面调查函数以了解在任何一种情况下我得到了什么。
  • 无论如何,我很感谢您的回复(我正在努力提高我在这门语言方面的技能——尤其是因为这样做会立即有利可图)而且我不知道 pcase-让

标签: elisp


【解决方案1】:

我能想到的两种方法:

  • 将“函数”foo 设为宏而非函数(如果可能)
  • 将一个新创建的 cons(或多个)传递给函数,并通过 setcar/setcdr 替换它们的 car 和 cdr

如果函数太复杂,您也可以结合这两种方法 - 使用宏 foo 创建 a 和 b 的 cons 并使用该 cons 调用函数 foo0,然后再次解包 car 和 cdr。

如果您需要超过 2 个 args,只需使用多个 cons 作为参数即可。

【讨论】:

  • 谢谢你,我喜欢 cons 单元的想法(我需要进一步阅读如何正确使用宏)
【解决方案2】:

只是为了向您展示如何做到这一点,但不要这样做,这是不好的风格。

(defun set-to (in-x out-y)
  (set out-y in-x))

(let (x)
  (set-to 10 'x)
  x)

但有时这不起作用:

(let (in-x)
  (set-to 10 'in-x)
  in-x)

有点像这个C++代码

void set_to(int x, int* y) {
    *y = x;
}

int y;
set_to(10, &y);

其实我希望 C++ 中没有非常量引用,这样 每个 mutator 都必须用上面的指针调用。

再一次,不要这样做,除非它是真的必要的。 请改用multiple-value-bindcl-flet

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-09
    • 1970-01-01
    • 2012-04-14
    • 2011-01-27
    • 2012-09-28
    相关资源
    最近更新 更多