Macros 我能摆脱这些评估吗?

Macros 我能摆脱这些评估吗?,macros,clojure,eval,Macros,Clojure,Eval,我想建立一个在Redis管道中执行的操作列表。我之所以使用它,是因为它比以前简单得多,但现在我需要连接池,因为加入时丢失了连接池,因此我再次关注carmine,它似乎是redis的首选和最完整的clojure库 我设法让它工作: ; require [taoensso.carmine :as redis] (defn execute1 [request] (redis/wcar {} (eval request))) (defmacro execute [body] `(redis/wcar {

我想建立一个在Redis管道中执行的操作列表。我之所以使用它,是因为它比以前简单得多,但现在我需要连接池,因为加入时丢失了连接池,因此我再次关注carmine,它似乎是redis的首选和最完整的clojure库

我设法让它工作:

; require [taoensso.carmine :as redis]
(defn execute1 [request] (redis/wcar {} (eval request)))
(defmacro execute [body] `(redis/wcar {} ~@(eval body)))

(def get1 `(redis/get 1))
(execute1 get1)
(execute [get1])
但是考虑到我将构建数千个元素的向量,我有点担心eval可能会对性能造成的影响,除了我被教导要尽可能避免eval之外。我认为defmacro是/可以在宏扩展时进行计算的,而宏扩展时可能早于AOT编译时?而且不使用eval

有什么我能做的吗?我应该搬到别的图书馆吗? 我看了一下carmine的源代码:我的痛苦只是因为给作者带来了一点便利:一个*上下文*用来避免传递一些额外的论点:摆脱它应该足够简单,但我没有在它上投入足够的资金。。。我还不如决定将来转移到另一个数据存储区

编辑:我被要求写一个我认为是样板的例子,我更愿意避免在我的实际代码中编写,因此:以下是未经测试的,它只是一个POC

(defn hset [id key val]
  #(redis/hset id key val))

(defn hsetnx [id key val]
  #(redis/hsetnx id key val))

(defn hincrby [id key increment]
  #(redis/hincrby id key increment))

(defn hgetall [id key]
  #(redis/hgetall id key))

(defn sadd [id el]
  #(redis/sadd id el))

(defn scard [id]
  #(redis/scard id))

(defn smembers [id]
  #(redis/smembers id))

(defmacro execute [forms]
  `(redis/wcar {} ~@(map apply forms)))

; end boilerplate

(defn munge-element [[a b c]]
  (conj
    (mapcat #(hincrby a :whatever %) b)
    (sadd c b)
    (hsetnx a c))

(defn flush-queue! [queue_]
  (execute queue_)
  [])

(defn receive [item]
  (if (< (count @queue) 2000)
    (swap! queue conj (munge-element item))
    (swap! queue flush-queue!)))

很明显,我可以写这样的东西,但如果这真的是打算使用carmine的方法,那么这些curry函数将与普通函数一起提供,或者替代它们。另外,通过使用语法引用构建DEF可以减少很多行,但这是偶然的复杂性,不是原始问题固有的。

以下代码与您的代码没有太大区别:

(defmacro execute [& forms]
   `(redis/wcar {} ~@forms))

(defn get1 []
  (redis/get 1))

(execute (get1) (get1) ...)
这就是自述中胭脂红的基本用法。如果它不适合你的需要,你能解释一下原因吗

在对问题进行编辑后,我想我有一个解决方案给你们。您正试图创建一个语句列表,以便在将来的某个特定时间执行。要实现这一点,您需要将每个语句包装在一个函数中,我同意,这是一个大量的样板文件

但是没有必要。通过使用自动为您创建Thunk的宏,可以获得相同的结果:

(defmacro statements [& forms]
  `(vector
     ~@(for [f forms]
         `(fn [] ~f))))
现在,无论您传递给该宏的是什么,都将产生一个零参数函数向量,可以使用以下方法进行计算:

(defn execute-statements [fns]
   (redis/wcar {} (mapv #(%) fns))
你的例子会变成这样:

(defn munge-element [[a b c]]
  (statements
    (mapcat #(redis/hincrby a :whatever %) b)
    (redis/sadd c b)
    (redis/hsetnx a c))

(defn flush-queue! [queue_]
  (mapv execute-statements queue_)
  [])
编辑:它在自己的管道中执行每个批。如果您想在单个队列中完成此操作,请使用concat而不是conj在receive and execute statements queue_uu而不是mapv execute statements queue_uu中构建队列

注:正确地说,IIRC:

(redis/wcar {} a [b c]])
返回与此相同的结果:

(redis/wcar {} a b c)

也就是说,carmine在某个地方收集结果,并始终为所有结果返回一个向量。即使没有,我想你还是可以通过稍微调整一下这里介绍的内容来避免你可怕的样板文件。

这样使用它非常麻烦:我必须这样做:1为我现在使用的每个redis函数创建一个curry函数我的代码中有hset、hsetnx、hincrby、hgetall、sadd、dbsize、scard,记忆,信息。。。这将相当于大量的样板文件,或者每次调用redis命令都要进行2次包装,更不用说您的execute有一个不同的API:为了使它变成一个稍微有用的形式,我必须使用类似于。。。defmacro execute[forms]`redis/wcar{}~@map apply forms,但一旦我这样做了,它实际上可能不会比我当前的解决方案更糟糕,它必须首先对任何redis命令的每次调用进行语法引用,以获得与执行相同的函数签名,只需在参数列表中省略&即可。然后它将接受任何顺序的命令。第二,如何涉及到大量的样板文件?如果不需要defn和参数列表[],只需使用def get1 redis/get 1之类的东西。所以,我只能假设我仍然没有得到你的要求。你能用一个很多样板的例子来扩展你的问题吗?我考虑过引用def的语法,但不是定义一个宏语句。。。对于我想做的事情来说,这似乎是一个很好的妥协。谢谢但是,如果我的用例不是非常不寻常,那么您认为将这些助手功能集成/提交到carmine upstream会有用吗?