clojure 宏来生成函数 [英] clojure macro to generate functions

查看:16
本文介绍了clojure 宏来生成函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试编写一个可以生成 n 个函数的宏.这是我到目前为止所拥有的:

I'm trying to write a macro that will generate n functions. Here's what I have so far:

; only defined this because if I inline this into make-placeholders
; it's unable to expand i# in  ~(symbol (str "_" i#))
(defmacro defn-from [str mdata args & body]
    `(defn ~(symbol str) ~mdata ~args ~@body))

; use list comprehension to generate n functions
(defmacro make-placeholders [n] 
    `(for [i# (range 0 ~n)] (defn-from  (str "_" i#) {:placeholder true} [& args] (nth args i#))))

; expand functions _0 ... _9
(make-placeholders 9)

我得到的错误是:

java.lang.ClassCastException: clojure.lang.Cons cannot be cast to java.lang.String

而且我不太确定这意味着什么,但我有一个模糊的概念,即(for ...)并没有像我认为的那样在宏中工作.

And I'm not really sure what that means, but I have this vague notion that (for ...) isn't working the way I think it is inside a macro.

推荐答案

您对运行时和编译时以及宏和函数之间的区别感到困惑.使用 eval 解决宏问题永远不是1正确答案:相反,请确保您返回的代码符合您的意愿.这是使您的原始版本正常工作的最小更改.

You are getting confused about the distinction between runtime and compile-time, and between macros and functions. Solving a macro problem with eval is never1 the right answer: instead, make sure that you return code that does what you want to happen. Here's a minimal change to make your original version work.

主要变化有:

  1. defn-from 是一个函数,而不是一个宏 - 您只需要一种方便的方法来创建列表,主宏负责将其插入到结果表单中.您需要这里的宏,因为您不希望它扩展到 make-placeholders 的主体中.

  1. defn-from is a function, not a macro - you just want a convenient way to create lists, which the main macro is responsible for inserting into the result form. You do not want a macro here, because you don't want it expanded into the body of make-placeholders.

make-placeholdersdo 开头,并在语法之外执行它的 for outside-引用.这是最重要的部分:您希望返回给用户的代码看起来像 (do (defn ...)),就好像他们是手动输入的一样 - 不是 (for ...),它只能定义一个函数.

make-placeholders starts with a do, and does its for outside of a syntax-quote. This is the most important part: you want the code returned to the user to look like (do (defn ...)), as if they'd typed it all in by hand - not (for ...), which could only ever def a single function.

<小时>

(defn defn-from [str mdata args & body]
    `(defn ~(symbol str) ~mdata ~args ~@body))

; use list comprehension to generate n functions
(defmacro make-placeholders [n]
  (cons `do
        (for [i (range 0 n)]
          (defn-from (str "_" i) {:placeholder true}
            '[& args]
            `(nth ~'args ~i)))))

user> (macroexpand-1 '(make-placeholders 3))
(do (clojure.core/defn _0 {:placeholder true} [& args] (clojure.core/nth args 0)) 
    (clojure.core/defn _1 {:placeholder true} [& args] (clojure.core/nth args 1)) 
    (clojure.core/defn _2 {:placeholder true} [& args] (clojure.core/nth args 2)))

1 非常非常罕见

您也可以完全不用宏来完成此操作,方法是使用函数来创建函数并使用低级操作 intern 而不是 def.事实证明,实际上要简单得多:

You can also do this completely without macros, by using a function to create functions and using the lower-level operation intern instead of def. It turns out to be much simpler, in fact:

(letfn [(placeholder [n]
          (fn [& args]
            (nth args n)))]
  (doseq [i (range 5)]
    (intern *ns* (symbol (str "_" i))
            (placeholder i))))

这篇关于clojure 宏来生成函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆