2

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

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

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

与其编写一个函数将可变数量的地图形式压缩在一起,我认为宏可能会解决问题。但是,我遇到了麻烦。

这个宏接受一个序列和一个列“掩码”

   (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"}

我会很感激我做错了什么的一些想法。当然,我可以根据需要提取的列数定制 a 函数。

谢谢你。

编辑:

修改宏

(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
4

1 回答 1

4

You are mixing up code that will execute at macro expansion with code that the macro will output. Until you enter a syntax-quote, you don't need to use auto-gensym (col#, mc# in your example). As for the macro output, it must always produce exactly one form. It seems that you are expecting it to produce several forms (one for each col), but that's not how this can work. Your macro output will currently look like

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

This is a form with two members. The member in the head position is expected to be a function, and the whole form is supposed to be evaluated as a function call.

The way to salvage this is to wrap your for in the macro with a vec and then use (apply zipmap (map-cols s1 cols)).

That answers your immediate question, but the solution will still not make sense: zipmap wants exactly two args, not a variable number of them as you said you want, and the output is a map, not a sequence that zips together your fields. Zipping is achieved using (map vector seq1 seq2 ...).

于 2012-04-17T13:46:19.783 回答