Binding 将向量作为绑定传递到for宏时出现问题

Binding 将向量作为绑定传递到for宏时出现问题,binding,clojure,list-comprehension,Binding,Clojure,List Comprehension,我想使用for宏处理任意数量的列表。我想创建一个函数,将向量作为绑定传递,因为列表的数量不同 如果我对绑定进行硬编码,它将按照我的预期工作: => (def list1 '("pink" "green")) => (def list2 '("dog" "cat")) => (for [A list1 B list2] (str A "-" B)) ("pink-dog" "pink-cat" "green-dog" "green-cat") 当我试图单独创建一个向量并将其用作

我想使用for宏处理任意数量的列表。我想创建一个函数,将向量作为绑定传递,因为列表的数量不同

如果我对绑定进行硬编码,它将按照我的预期工作:

=> (def list1 '("pink" "green"))
=> (def list2 '("dog" "cat"))
=> (for [A list1 B list2] (str A "-" B))
("pink-dog" "pink-cat" "green-dog" "green-cat")
当我试图单独创建一个向量并将其用作绑定时,我遇到了一些问题。在这里,我手动创建绑定向量:

=> (def testvector (vec (list 'A list1 'B list2)))
这似乎很好:

=> testvector
[A ("pink" "green") B ("dog" "cat")]
=> (class testvector)
clojure.lang.PersistentVector
但是,

=> (for testvector (str A "-" B))
#<CompilerException java.lang.IllegalArgumentException: for requires a vector for its binding (NO_SOURCE_FILE:36)>
=>(用于测试向量(str A“-”B))
#
我不明白为什么testvector在用作中的绑定时不被视为向量。抓着稻草,我把testvector放在方括号中,这样可以让for宏满意(它看到一个向量),但是现在我有一个带有一个元素的向量(即向量中的向量),这不起作用,因为绑定需要是名称和集合对

=> (for [testvector] (str A "-" B))
#<CompilerException java.lang.IllegalArgumentException: for requires an even number of forms in binding vector (NO_SOURCE_FILE:37)>
=>(对于[testvector](str A“-”B))
#
任何关于如何将向量作为绑定动态传递给for的建议都将不胜感激。

关键在于for是一个宏。在宏展开时,testvector是一个符号。它将在求值时求值为向量,但从for宏的角度来看,它不是向量


如果您检查for宏的源代码(在core.clj中),您将看到for使用了一个无引号的向量?调用,就像上面示例中的tst一样。

这里是最后的方法。请注意,无论您在哪里看到
读取字符串
,这都是龙的代码!(由于存在安全风险,并且对代码的行为缺乏编译时一致性保证)


虽然不是解决您问题的方法,但应该注意的是,您正在做的事情更容易通过map实现,而不是例如

user=> (def list1 '("pink" "green"))
#'user/list1
user=> (def list2 '("dog" "cat"))
#'user/list2
user=> (map #(str %1 "-" %2) list1 list2)
("pink-dog" "green-cat")
user=> 

在学习和实验时,另一个有用的技巧是使用关键字而不是字符串。这可以减少键入,即不需要将值放在引号中,有时可以帮助更容易地识别错误。您只需执行(def list1’(:pink:green))即可,而不必执行(def list1’(:pink:green))。更好的方法是,不要使用列表,而是尝试使用向量,然后不必引用它(保存另一次击键)

您可以尝试强制计算绑定向量。不要试图定义一个将为宏包装
的宏,而是将其包装在函数中,例如

(defn for-fn [bindings expr]
  (eval `(for ~bindings ~expr))) 
然后,您可以使用一些额外的约束构建一个绑定向量,因为绑定向量中的所有s表达式都需要有效并且包含一个动词作为第一个元素

(let [bindings '[a (list 1 2) b (list 3 4) c (range 10 12)
                 :when (> (+ a b c) 15)]
      expr '(str a "-" b "-" c)]
  (for-fn bindings expr)) 
以你为例:

(def list1 '("pink" "green"))
(def list2 '("dog" "cat"))
(def testvector (vector 'A (cons 'list  list1) 'B (cons 'list list2)))

(for-fn testvector '(str A "-" B))
=> ("pink-dog" "pink-cat" "green-dog" "green-cat")

注意:由于fn的
是函数,您需要引用表达式
(str A“-”B)
,以防止早期评估(在绑定A&B之前)。

(vec(list…)可以更简单地写成(vector…)。对于我们这些仍在学习Clojure的人来说,下一步是什么使它真正起作用?我已经尝试了(defmacro-combo[v]`(for~v[ab]),但它不能处理相同的关于for-A的错误消息vector@JonathanBenn是的,即使在宏中换行,它本身仍然是一个宏,同样的限制也适用。这个答案不是解决方案,只是解释为什么它不起作用。现在我想不出一个聪明的宏时间解决方案,但我认为你可以用递归函数解决OP的测试问题。谢谢。我一直在努力寻找解决方案,但我的Clojure fu不够先进。我将尝试你关于递归函数的建议。不要使用字符串来操作lisp中的任何代码。这是一个lisp,所以您只需执行
(eval`(对于~testvector vec(str~'a“-”~'B))
(let [bindings '[a (list 1 2) b (list 3 4) c (range 10 12)
                 :when (> (+ a b c) 15)]
      expr '(str a "-" b "-" c)]
  (for-fn bindings expr)) 
(def list1 '("pink" "green"))
(def list2 '("dog" "cat"))
(def testvector (vector 'A (cons 'list  list1) 'B (cons 'list list2)))

(for-fn testvector '(str A "-" B))
=> ("pink-dog" "pink-cat" "green-dog" "green-cat")