Clojure:列表中变量的名称

时间:2017-12-14 11:44:30

标签: clojure

我有这样的事情:

(def a "text")
(def b "text")
(def c nil)
(def d 8)
(def e "")

(def testlist (list a b c d e ))

现在,有没有办法获取变量名称的字符串?我假设没有'没有'是最可能的答案。

name似乎不起作用,因为它只返回值。列表是否仅包含def之后的值?

编辑:我忘记了这个问题可能是必不可少的:我既不能使用eval,也不能使用defmacro,两者都不允许(出于安全等原因)。所以,是的......

3 个答案:

答案 0 :(得分:1)

您将无法从变量名称中获取字符串,因为Clojure会尽快评估它们以生成testlist

=> (def testlist (a b c d e ))
("text" "text" nil 8 "")

但是,您可以quote the list检索与每个变量名称相关联的符号

=> (def testlist (quote (a b c d e ))) ;; equivalent to '(a b c d e ))
(a b c d e)

然后使用str函数将这些符号转换为字符串

=> (map str testlist)
("a" "b" "c" "d" "e")

稍后,您可以eval此列表来检索命名空间上下文中的值

=> (map eval testlist)
("text" "text" nil 8 "")

请注意,将eval与外部输入(例如read-line)一起使用可以创建a security risk in Clojure and other languages

此外,必须在与其定义相同的命名空间中评估列表。否则,Clojure将无法解析符号。

=> (ns otherns)
=> (map eval user/testlist)
 java.lang.RuntimeException: Unable to resolve symbol: a in this context

大多数情况下的最佳做法是使用macros

答案 1 :(得分:1)

你可以用宏来做这件事(只是为了好玩。我认为它根本不是一个可行的用例)。

user> (defmacro list+ [& syms]
        `(with-meta (list ~@syms) {:vars (quote ~syms)}))
#'user/list+

user> (def testlist (list+ a b c d e))
#'user/testlist

user> (-> testlist meta :vars)
(a b c d e)

user> (defn nil-vals-names [data]
        (for [[v name] (map vector data (-> data meta :vars))
              :when (nil? v)]
          name))
#'user/nil-vals-names

user> (nil-vals-names testlist)
(c)

答案 2 :(得分:0)

目前还不清楚你想要实现的目标,还有一种可能的方法。

meta函数获取引用并返回带有:name字段的映射,该字段包含变量名称的字符:

user=> (def foo 42)
#'user/foo

user=> (meta #'foo)
{:line 1, :column 1, 
 :file "/some/tmp/file.clj", 
 :name foo, 
 :ns #namespace[user]}