请教一个`symbol-value`的问题,帮忙看下这是不是一个bug?

如下函数,

(defun symbol-value-test ()
  (-let (([a b c d e]
          [1 2 3 4 5]))
    (--map (cons (symbol-name it) (symbol-value it)) '(a b c d e))))

当我如下直接eval函数时,返回正确结果: (("a" . 1) ("b" . 2) ("c" . 3) ("d" . 4) ("e" . 5))

(symbol-value-test)

但是当我edebug-defun该函数后,运行到(symbol-value it)时,会显示下面错误:

lispy--eval-elisp: Symbol’s value as variable is void: a

这会不会是一个潜在的emacs bug?

Emacs version:GNU Emacs 28.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.23, cairo version 1.16.0) of 2021-01-01

不要用symbol-value访问let指定的变量,在lexical binding下是失效的。

Return SYMBOL's value.  Error if that is void.

Note that if lexical-binding is in effect, this returns the
global value outside of any lexical scope.

感谢,学习到了。这种情况下有什么解决方法吗?单独用it的话,只能访问到symbol name,无法获得其值。

(defun symbol-value-test ()
  (-let (([a b c d e]
          [1 2 3 4 5]))
    (--map (cons (symbol-name it) it) '(a b c d e))))

在debug模式下,返回的结果是,(("a" . a) ("b" . b) ("c" . c) ("d" . d) ("e" . e))

你的 -let--map 中的 abcd 完全是不同的东西。去掉 -let 也不影响目前的返回结果。

合并两个 list 可以用 -zip

(-zip '(a b c d) '(1 2 3 4))
;; => ((a . 1) (b . 2) (c . 3) (d . 4))

(--zip-with (cons (symbol-name it) other) '(a b c d) '(1 2 3 4))
;; => (("a" . 1) ("b" . 2) ("c" . 3) ("d" . 4))

原来如此。用-let是为了举例子方便,上面的a,b,c,d并不是单纯的数字,需要在zip之前被计算,我的需求是如何获取到被计算的值。

除了给 Emacs lisp 写开发工具,还会有什么场景用到这种需求吗?单纯写个对表达式求值的小工具,完全可以用 assoc list lookup 解决

用哈希表存symbol然后查表。

比如以下场景。

(defun symbol-value-test ()
  (-let (([a b c d e]
          [1 2 3 4 5]))
    (s-format "abcde\n${a}-${b}-${c}-${d}-${e}" 'aget 
              (--map (cons (symbol-name it) (symbol-value it)) '(a b c d e)))))

我希望得到

abcde
1-2-3-4-5

实际的需求会比这个更复杂一些,如果这种方式可以实现的话,我就不需要把(a b c d e)写两遍了。 比如按照-zip的话,我需要写成,

(defun symbol-value-test ()
  (-let (([a b c d e]
          [1 2 3 4 5]))
    (s-format "abcde\n${a}-${b}-${c}-${d}-${e}" 'aget 
              (-zip '("a" "b" "c" "d" "e") (list a b c d e)))))

如果参数增加的话,我需要修改两个list,感觉不精简。

(require 's)
(require 'ht)

(let ((ht (ht
           ("a" 1)
           ("b" 2)
           ("c" 3)
           ("d" 4)
           ("e" 5))))
  (s-format "abcde\n${a}-${b}-${c}-${d}-${e}" 'gethash ht))
(let ((a '(a b c d e))
      (b '(1 2 3 4 5)))
  (s-format "abcde\n${a}-${b}-${c}-${d}-${e}" 'aget (-zip a b)))

有什么理由这么写不能滿足你的需求嘛?

都怪我举的例子把需求带偏了。a b c d e都是需要被计算的,如下所示:

(defun symbol-value-test ()
  (let* ((a 1)
         (b (+ a 5))
         (c (* a b))
         (d (+ a b c))
         (e (/ c d)))
    (s-format "abcde\n${a}-${b}-${c}-${d}-${e}" 'aget 
              (-zip '("a" "b" "c" "d" "e") (list a b c d e)))))

多谢,如我最新的回复,每个值都要事先被计算,如果ht的话还是需要把所有计算好的数值放到一个list里面去,并没有达到简化的效果。

format template不用动态生成的话可以用s-lex-format

(defun symbol-value-test ()
  (let* ((a 1)
         (b (+ a 5))
         (c (* a b))
         (d (+ a b c))
         (e (/ c d)))
    (s-lex-format "abcde\n${a}-${b}-${c}-${d}-${e}")))

我大概明白了,你应该是想达成类似Python f-string的效果

1赞

原来这样的函数真的存在。。。 太感谢了。

它是宏。函数没法动态的访问(比如没有lexical-symbol-value这种东西)Elisp词法作用域里的值。所以template得是静态的,Python的f-string也是一样必须得静态的string template.

1赞

受教了!我去研究研究。

如果要变化template的话,应该如何快速类似下面的需求呢?

(defun symbol-value-test ()
  (let* ((a 1)
         (b (+ a 5))
         (c (* a b))
         (d (+ a b c))
         (e (/ c d))
         (format1 (if (= b 6)
                     "abcde\n${a}-${b}-${c}-${d}-${e}"
                   "abcde\n${a}-${b}-${c}-${e}-${d}")))
    (s-lex-format format1)))
(defun symbol-value-test ()
  (let* ((a 1)
         (b (+ a 5))
         (c (* a b))
         (d (+ a b c))
         (e (/ c d)))
    (if (= b 6)
        (s-lex-format "abcde\n${a}-${b}-${c}-${d}-${e}")
      (s-lex-format "abcde\n${a}-${b}-${c}-${e}-${d}"))))

谢谢。因为这个template很复杂,所以被定义在函数外。希望用类似(s-lex-format template)实现,不知道有没有可行性方案?

(symbol-macrolet ((%spec-a% (s-lex-format "abcde\n${a}-${b}-${c}-${d}-${e}"))
                  (%spec-b% (s-lex-format "abcde\n${a}-${b}-${c}-${e}-${d}")))

  (defun symbol-value-test ()
    (let* ((a 1)
           (b (+ a 5))
           (c (* a b))
           (d (+ a b c))
           (e (/ c d)))
      (if (= b 6)
          %spec-a%
        %spec-b%))))

我的建议是把完整的代码贴出来(比如 pastebin.com)再找人提意见,这样问很难得到最合适的解法。可能你实际需要的和你觉应该问的是两回事

1赞