编写Clojure宏来生成地图表格

时间:2012-04-17 13:34:54

标签: clojure

我有一系列序列,从clojure-csv输出。

(def s1 [[:000-00-0000 "SMITH" "JOHN" 27][:000-00-0001 "FARMER" "FANNY" 28]])

我有一个列号[0 3]的向量,我想用它来从每个序列中提取数据。

我没有编写一个函数来将可变数量的地图形式压缩在一起,而是认为宏可能会成功。但是,我遇到了麻烦。

此宏接受序列和列“mask”

   (defmacro map-cols [seq & columns]
    (for [col# columns
        :let [mc# `(map #(nth % ~col# nil) ~seq)]]
        mc#))

(map-cols s1 cols)
ClassCastException clojure.lang.LazySeq cannot be cast to clojure.lang.IFn  bene-csv.core/eval2168 

我希望生成出现的多个地图表单 以下内容:

(zipmap (map #(nth % 0 nil) s1) (map #(nth % 1 nil) s1))
{:000-00-0001 "FARMER", :000-00-0000 "SMITH"}

我很欣赏我做错的一些想法。当然,我可以根据需要提取的列数来定制函数。

谢谢。

编辑:

修改后的宏

(defmacro map-cols [seq & columns]
    (vec (for [col columns
        :let [mc `(map #(nth % ~col nil) ~seq)]]
        mc)))


(apply zipmap (map-cols s1 cols))

ArityException Wrong number of args (1) passed to: core$zipmap  clojure.lang.AFn.throwArity

1 个答案:

答案 0 :(得分:4)

您正在混合将在宏扩展时执行的代码与宏将输出的代码。在输入语法引用之前,您不需要使用auto-gensym(在您的示例中为col#,mc#)。至于宏输出,它必须始终只生成一个表单。您似乎期望它生成多个表单(每个表单一个),但这不是如何工作的。您的宏输出目前看起来像

((map #(nth % 0 nil) s1) (map #(nth % 1 nil) s1))

这是一个有两名成员的表格。头部位置的成员应该是一个函数,整个表单应该被评估为函数调用。

挽救此问题的方法是使用forvec包裹在宏中,然后使用(apply zipmap (map-cols s1 cols))

这回答你的直接问题,但解决方案仍然没有意义:zipmap只需要两个args,而不是你想要的可变数量的args,输出是一个地图,而不是一个序列将你的领域拉到一起。使用(map vector seq1 seq2 ...)实现压缩。