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
Clojure宏:引用、取消引用和评估_Clojure_Macros_Quoting - Fatal编程技术网

Clojure宏:引用、取消引用和评估

Clojure宏:引用、取消引用和评估,clojure,macros,quoting,Clojure,Macros,Quoting,我有以下代码: (ns macroo) (def primitives #{::byte ::short ::int}) (defn primitive? [type] (contains? primitives type)) (def pp clojure.pprint/pprint) (defn foo [buffer data schema] (println schema)) (defmacro write-fn [buffer schema schemas] (l

我有以下代码:

(ns macroo)

(def primitives #{::byte ::short ::int})

(defn primitive? [type]
  (contains? primitives type))

(def pp clojure.pprint/pprint)

(defn foo [buffer data schema]
  (println schema))

(defmacro write-fn [buffer schema schemas]
  (let [data (gensym)]
    `(fn [~data]
       ~(cond
          (primitive? schema) `(foo ~buffer ~data ~schema)
          (vector? schema) (if (= ::some (first schema))
                             `(do (foo ~buffer (count ~data) ::short)
                                 (map #((write-fn ~buffer ~(second schema) ~schemas) %)
                                       ~data))
                             `(do ~@(for [[i s] (map-indexed vector schema)]
                                      ((write-fn buffer s schemas) `(get ~data ~i)))))
          :else [schema `(primitive? ~schema) (primitive? schema)])))) ; for debugging

(pp (clojure.walk/macroexpand-all '(write-fn 0 [::int ::int] 0)))
问题是,在计算最后一个表达式时,我得到

=>
(fn*
 ([G__6506]
  (do
   [:macroo/int :macroo/int true false]
   [:macroo/int :macroo/int true false])))
如果有必要,我将解释代码,但现在我只说明问题(可能只是我犯的新手错误):

在:else分支中,分别返回true和false,因为我在cond表达式中使用了第二个版本,所以它在不应该的地方失败(我更喜欢第二个版本,因为如果我没有弄错的话,它将在编译时进行计算)

我怀疑这可能与名称空间限定的符号有关?

经过一些调查(参见编辑),这里是一个可行的Clojure替代方案。基本上,您很少需要递归宏。如果你 需要递归地构建表单,委托给辅助函数并从宏调用它们(而且,
write fn
不是一个好名字)

(defmacro write fn[缓冲区模式]
我们只评估一次“缓冲”和“乐趣”
对于中间变量,我们需要gensym。
(让[fsym(gensym)
bsym(gensym)]
定义两个相互递归的函数
;解析并构建由两个键组成的映射
;;
;;;-args是生成函数的参数列表
;;;-body是生成表单的列表
;;
(letfn[(转换器[模式]
(续)
(原语?模式)
(让[g(gensym)]
{:args g
:body`(~fsym~schema~bsym~g)})
(顺序?模式)
(如果(和(=(计数模式)2)
(=(第一个架构)::一些)
(原语?(第二模式)))
(让[g(gensym)]
{:args['&g]
:正文
`(doseq[i#~g]
(~fsym~(第二模式)~bsymi#)})
(reduce reducer{:args[]:body[]}架构)
:else(抛出(异常。“错误输入”))
(reducer[{:keys[args body]}架构)
(let[{arg:args代码:body}(转换器架构)]
{:args(conj-args-arg)
:body(conj body code)}]
(let[{:keys[args body]}(转换器模式)]
`(让[~fsym~开心点
~bsym~缓冲区]
(fn[~args]~@body(()()))
宏接受一个缓冲区(不管它是什么)、一个由您的语言定义的模式以及一个为生成的函数访问的每个值调用的函数

例子
(pp(宏扩展)
"(写fn 0)
[::int[::some::short][::int::short::int]]
(fn[&更多](应用println more()()))
。。。产生以下结果:

(让*
[G__1178(fn[&更多](应用println更多))G_1179 0]
(clojure.core/fn)
[[G_uu1180[&G_u1181][G_u1182 G_u1183 G_u1184]]
(G__1178:Macro/int G__1179 G__1180)
(clojure.core/doseq)
[i_uuu1110_uuuauto_uuuug_uuu1181]
(G__1178:Macro/short G__1179 i__1110__自动__))
[(G__1178:Macro/int G__1179 G__1182)
(G__1178:Macro/short G__1179 G__1183)
(G_uu1178:Macro/int G_u1179 G_u1184)])
  • 首先,评估buffer和fun并将它们绑定到局部变量
  • 返回一个闭包,该闭包接受一个参数并根据给定的模式对其进行分解,这要归功于Clojure的分解功能
  • 对于每个值,使用适当的参数调用fun
  • 当模式为
    [::some x]
    时,接受零个或多个值作为向量,并为每个值调用函数fun。这需要通过循环完成,因为只有在调用函数时才知道大小
如果我们将向量
[32[1 3 4 5 6 7][2 55 1]
传递给上述宏展开生成的函数,则会打印以下内容:

:宏操作/int 0 32
:macro/short 0 1
:Macro/short 0 3
:Macro/short 0 4
:Macro/short 0 5
:Macro/short 0 6
:Macro/short 0 7
:macro/int 0 2
:macro/short 0 55
:macro/int 0 1
在这行中:

`(do ~@(for [[i s] (map-indexed vector schema)]
         ((write-fn buffer s schemas) `(get ~data ~i)))))
您正在调用当前范围内的宏
write fn
,其中
s
只是一个符号,而不是
schema
中的一个条目。相反,您希望发出将在调用方作用域中运行的代码:

`(do ~@(for [[i s] (map-indexed vector schema)]
         `((write-fn ~buffer ~s ~schemas) (get ~data ~i)))))
并对
的另一个分支进行类似的更改(如果


顺便说一句,乍一看,这并不一定是一个宏,而是一个高阶函数:接受一个模式或任何东西,然后返回一个数据函数。我猜你是把它当作性能的宏来做的,在这种情况下,我建议你先用缓慢、简单的方法来尝试;一旦你有工作,你可以使它成为一个宏,如果必要的。或者,也许我错了,这里有一些基本上必须是宏的东西。

函数的目的是:对于给定的“模式”,创建一个采用数据结构(由模式描述)并将其转换为字节的函数。目标是在编译时创建此函数,并通过内联函数参数使其尽可能密集。因此-最佳情况下-宏扩展函数不会包含类似((fn[x]x)y)的列表。我编辑了答案(我最终会清理所有的东西,但现在我正试图找出真正的问题所在),虽然我不能真正摸索CL代码(从未摆弄过),但据我所知,它看起来确实很有希望。那个破坏性的东西我还没研究过。关于::some case:[::some::int]基本上意味着数据结构包含一些int,其计数在编译时是未知的(这是我为自己定义的一些自定义DSL)。关于您的上一次编辑:您的代码看起来很漂亮,尽管它需要一些时间来消化!如果我有进一步的问题,我会回复你(希望你不介意)。有一个小问题:“::some”后面可以跟任何有效的模式,而不仅仅是primiti
`(do ~@(for [[i s] (map-indexed vector schema)]
         ((write-fn buffer s schemas) `(get ~data ~i)))))
`(do ~@(for [[i s] (map-indexed vector schema)]
         `((write-fn ~buffer ~s ~schemas) (get ~data ~i)))))