Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Macros 在带有参数的clojure宏中使用匿名函数_Macros_Clojure - Fatal编程技术网

Macros 在带有参数的clojure宏中使用匿名函数

Macros 在带有参数的clojure宏中使用匿名函数,macros,clojure,Macros,Clojure,mydata是关于用户姓名和性别的: (def mydata [["a" 'f] ["b" 'm]]) 我想要的是: (group-by #(let [[name gender] %1] name) mydata) ; {"a" [["a" f]], "b" [["b" m]]} 而且: (group-by #(let [[name gender] %1] gender) mydata) ; {f [["a" f]], m [["b" m]]} 所以我想构建一个这样的函数: (defn m

mydata是关于用户姓名和性别的:

(def mydata [["a" 'f] ["b" 'm]])
我想要的是:

(group-by #(let [[name gender] %1] name) mydata)
; {"a" [["a" f]], "b" [["b" m]]}
而且:

(group-by #(let [[name gender] %1] gender) mydata)
; {f [["a" f]], m [["b" m]]}
所以我想构建一个这样的函数:

(defn my-group-by [data, field]
  (group-by #(let [[name gender] %1] field) mydata))
但它出了问题

(mygroup-by mydata 'name)
; {name [["a" :F] ["b" :M]]}
然后我想宏可以做到这一点

(defmacro my-macro [data field]
  `(group-by #(let [[name,gender] %1] ~field) ~data))
然后我运行它

(my-macro mydata name)
; CompilerException java.lang.RuntimeException: Can't let qualified name: clojure.core/name, compiling:(/tmp/form-init2648255959159095748.clj:1:1) 
为什么??我错在哪里?

对于宏错误:

在宏中,必须将局部变量后缀为#,以避免作为宏参数传递的值出现范围问题。。。不能从宏外部引用宏内部定义的变量。。。这正是你想要做的。宏参数是值,不像C宏那样是重写过程

(defmacro my-macro [data field]
  (group-by #(let [[name# gender#] %1] ~field) ~data))
您必须将name#或gender#传递给宏,但它没有意义,因为它没有在宏之外定义。。。因此,缩略图给出的解决方案是正确的

但您可以这样做(只是为了获取信息,因为这不是一个正确的解决方案):

但在这种情况下,正如alfredx所写的,显然更容易做到,但我更喜欢使用关键词:

(defn my-fn [data field]
  (group-by #(nth % (.indexOf [:name :gender] field)) data))

没有必要为此使用宏

如果将数据表示为地图序列:

(def mydata [{:name "a", :gender :F}
             {:name "b", :gender :M}
             {:name "a", :gender :M}]) ; extra line
。。。然后,您可以使用地图的关键字作为函数:

(group-by :gender mydata)
{:F [{:gender :F, :name "a"}],
 :M [{:gender :M, :name "b"} {:gender :M, :name "a"}]}

如果地图速度太慢,您可以将其转换为记录:

(defrecord user [name gender])

(def mydata [(map->user {:name "a", :gender :F})
             (map->user {:name "b", :gender :M})
             (map->user {:name "a", :gender :M})])

。。。这和地图一样有效

在这里使用宏有点过分了。 使用map和按键排序更为常见。 如果确实需要使用自己的向量结构进行排序,以下是一种方法:

(defn my-group-by [data field] 
  (group-by 
    #(nth % (.indexOf ['name 'gender] field))
    data))
然后,像这样使用它

(my-group-by mydata 'name)
(my-group-by mydata 'gender)

然而,我个人会用“name”替换所有出现的“name”,用“gender”替换所有出现的“gender”。

map是可以的,但它会浪费太多的内存。我有2000万条记录,使用vector和
#(第n%(.indexOf['name'gender]field))
是匹配的betterOk,早上更好。我只是给出了宏错误的原因。我认为在这里使用auto-gensym不起作用,因为它创建了一个gensym,您只能从引用的语法形式中引用它——本质上创建了一个“局部变量”,如果您愿意,它只能在宏本身的定义中使用。我相信这里的解决方案是,如果您想编写一个宏,要求用户使用
name
gender
作为符号名,那么可以使用quote+unquote,即
[~'name~'gender]
。我相信这些将解决
user/name
user/gender
,或者无论ns名称如何代替
user
。也就是说,我同意使用宏的麻烦比在这里值得的多。有关更好的方法,请参阅其他解决方案。
(defn my-group-by [data field] 
  (group-by 
    #(nth % (.indexOf ['name 'gender] field))
    data))
(my-group-by mydata 'name)
(my-group-by mydata 'gender)