将seq-exprs传递给使用引号

时间:2017-08-25 13:55:22

标签: clojure

我正在尝试使用引文将seq-exprs传递给for,但我的两次尝试都会产生CompilerException java.lang.IllegalArgumentException: for requires a vector for its binding。我显然误解了引用和评估(及相关概念)的工作原理。

(let [v ['b (range 2)]]
   (for v b))

(let [v '[b (range 2)]]
   (for v b))

这样做的正确方法是什么?如何向开始者解释为什么上述任何实现都不起作用?谢谢!

编辑:
我知道我可以做到

(let []
   (for [b (range 2)] b))

我想要实现的是能够传递绑定的向量(例如,另一个函数的输出)

编辑2,动机/背景:
我正在构建一个转换器[repo],我正在尝试翻译和展开for循环(包括嵌套循环,example),所以我认为我可以追加要迭代的变量范围通过嵌套循环更深入地遍历AST(抽象语法树),然后评估在给定生成的“绑定”的每个原始块中包含的翻译关系(关系总是可以逐个翻译)到seq-exprs通过clojure for。如果有“更好”的方法,你有建议吗?

2 个答案:

答案 0 :(得分:3)

这正是宏和函数之间的区别。函数在运行时获取和返回数据。但是,宏在编译时接受并返回代码

请考虑以下事项:

(defn foo [arg]
  (str arg))

(let [x (dec 10)]
  (foo x)) ;=> "9"

foo将整数9视为参数,因为x的当前运行时值为9.但是,foo无法知道值9由符号x表示,因为符号x是代码。

(defmacro bar [arg]
  (str arg))

(let [x 9]
  (bar x)) ;=> "x"

(str 'x) => "x"

因此,如果我们将其更改为宏,我们会得到x的编译时间值,它只是符号x

(我们将其更改为字符串,然后将其作为代码返回,并将字符串"x"作为代码评估为"x"。)

这里重要的一点是,foo以同样的方式知道它被传递了一个名为x的东西,它只知道运行时值是9,{{1} } 只有知道它传递了一个名为bar的东西,它无法知道x的运行时值是什么。

这就是宏传染的原因。

因此,在您的示例中,x运行时值为v,但传递给{{的编译时值1}}只是符号'[b (range 2)]本身。

要解决您的直接问题,您可以执行以下操作:

for

但那只是......太可怕了。你可能想要的是写一个宏。

我不确定这个宏会是什么样子,因为我必须了解你想要解决的实际问题,但这里有一个宏的例子,它采用了seq-将它们传递给v

(let [v '[b (range 2)]]
  (eval `(for ~v ~'b)))

答案 1 :(得分:0)

['b (range 2)] => ['b (0 1)]在此示例中,您有一个名为b的符号,并且您正在调用函数(range 2),该函数会使(0 1)

在此示例中,

'[b (range 2)] => [b (range)]您引用了所有表达式,因此您最终得到了符号brange symbol within a list data structure

关于你的for循环,你得到了那个例外,因为你没有正确使用它(缺少矢量绑定)。

这是你应该编写for循环的方法:

(for [i (range 5)] ;;you need to define vector in here
     (do 
         (println "I: " i) 
         i));;let's return i here

结果:

I: 0
I: 1
I: 2
I: 3
I: 4
=> (0 1 2 3 4)