【问题标题】:Clojure Nested MacroClojure 嵌套宏
【发布时间】:2014-06-06 21:57:16
【问题描述】:

是否可以宏返回宏?我想最大限度地简化代码,我可以使用返回函数的宏来做到这一点。但是,它的开销太大而且太慢。为了使代码更具可读性,我没有使用类型提示,但即使使用它们,我的代码也会慢约 5 倍。

我的英语不是很精确,所以我写了我有什么和我想有什么。

我有这个……

(defmacro series [java-array]
  `(fn 
     ([i#]
      (aget ~java-array i#))
     ([start# stop#]
      (let [limit# (unchecked-subtract-int stop# start#)
            result# (double-array limit#)]
       (loop [i# 0]
        (if (< i# limit#)
          (let [r# (double (aget ~java-array i#))]
            (aset result# i# r#)
            (recur (inc i#)))
          result#))))))

我想要这样的东西......

(defmacro series [java-array]
  `(defmacro blabla 
     ([i#]
      `(aget ~~java-array i#))
     ([start# stop#]
      `(let [limit# (unchecked-subtract-int stop# start#)
             result# (double-array limit#)]
         (loop [i# 0]
           (if (< i# limit#)
             (let [r# (double (aget ~~java-array i#))]
               (aset result# i# r#)
               (recur (inc i#)))
             result#))))))

但是当我这样称呼它时......

Wrong number of args (1) passed to: blabla

一个更简单的例子。

我有很多 java 数组。我不想用aget。我希望宏扩展到(aget array-name i)。我编写了一个扩展为(fn [n] (aget array-name i)) 的宏,但这是不必要的开销。

(defmacro series [arr]
  `(fn [i#]
     (aget (longs ~arr) (int i#))))

我现在声明系列,比如“日期”,并以这种方式调用它(date i),这将返回给我数组的“i”元素。

【问题讨论】:

  • 您可以在宏内部调用函数,这些函数在编译时运行并帮助您操作作为参数传递给宏的数据结构(源代码)。我想这可能就是你要找的。不过,从您的示例中,我不太明白您在做什么。你能举个更简单的例子吗?

标签: macros clojure nested


【解决方案1】:

我认为您正在寻找的是一种在函数中声明本地宏的方法。 clojure.tools.macro 包有一个 macrolet 表单,我认为您应该能够为本地数组查找创建一个宏。

这是我整理的一个小例子:

; project.clj
(defproject testproject "1.0.0-SNAPSHOT"
  :description "FIXME: write description"
  :dependencies [[org.clojure/clojure "1.5.0"]
                 [org.clojure/tools.macro "0.1.2"]])

; src/testproject/core.clj
(ns testproject.core
  (:require [clojure.tools.macro :refer [macrolet]]))

(defn my-test []
  (let [n 1000
        arr (int-array n (range 10))]
    (macrolet [(lookup [i] (list `aget 'arr i))]
      (loop [i 0, acc 0]
        (if (< i n)
          (recur (inc i) (+ acc (lookup i)))
          acc)))))

在示例中,我使用macrolet 声明了一个名为lookup 的宏,这将导致(lookup 5) 扩展为(lookup arr 5),这就是我认为您正在寻找的。​​p>

请注意,在本地宏定义中引用 arr 时必须小心。如果您刚刚将宏声明为`(aget arr ~i),那么它会尝试查找不存在的完全限定符号arr。您也可以将宏声明为`(aget ~'arr ~i),但我觉得(list `aget 'arr i) 看起来好多了。

现在您可以更进一步,使用defmacro 创建一个包含macrolet 的宏,您可以使用它来简化带有本地查找宏的数组声明。 我们将此作为练习留给读者。

由于宏是如此微妙,而在宏中嵌套宏只会让事情变得更糟,我决定只举一个例子让你弄清楚它是如何工作的可能会更有帮助:

(defmacro lookup-binding [[fn-sym value] & body]
  (let [array-sym (symbol (str fn-sym "-raw"))]
    `(let [~array-sym ~value]
       (macrolet [(~fn-sym [i#] (list aget '~array-sym i#))]
         ~@body))))

(defn my-test2 []
  (let [n 1000]
    (lookup-binding [arr (int-array n (range 10))]
      (loop [i 0, acc 0]
        (if (< i n)
          (recur (inc i) (+ acc (arr i)))
          acc)))))

免责声明:我只是想表明这是可能,而不是一个好主意。我个人认为所有这些额外的复杂性都不值得避免aget。我建议只使用aget,因为它只是几个额外的字符,它会让你的代码更清晰/可读。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-25
    相关资源
    最近更新 更多