首页 > 解决方案 > 是否可以定义一个宏:在函数上调度其他任何东西?

问题描述

我不知道如何真正命名我的问题,因为我什至不知道如何开始考虑这个问题,所以我会陈述这个问题。想象一下,我有几个使用相同语法的 Java 对象的静态方法,例如:

https://github.com/deeplearning4j/nd4j/blob/master/nd4j-backends/nd4j-api-parent/nd4j-api/src/main/java/org/nd4j/linalg/ops/transforms/Transforms.java

/**
 * Floor function
 *
 * @param ndArray
 * @return
 */
public static INDArray floor(INDArray ndArray, boolean dup) {
    return exec(dup ? new Floor(ndArray.dup()) : new Floor(ndArray));

}

/**
 * Signum function of this ndarray
 *
 * @param toSign
 * @return
 */
public static INDArray sign(INDArray toSign, boolean dup) {
    return exec(dup ? new Sign(toSign, toSign.dup()) : new Sign(toSign));
}

所以这是一个示例虚拟包装器:

(defn floor
 ^INDArray
  [^INDArray a ^boolean dup]
  (Transforms/floor a dup))

(defn sign
 ^INDArray
  [^INDArray a ^boolean dup]
  (Transforms/sign a dup))

这里的问题是您浪费时间编写具有相同模板的函数,这在第一次是可以的......但是假设您想在更改/损坏的代码/性能调整/其他情况下更新所有这些。
我搜索了这个问题,但一无所获。理想的情况是(宏?),例如:

(defoperator floor Transforms/floor)

或者

(def floor (->operator Transforms/floor))

我不知道是否可以为初学者调用“通用”静态方法,即使这是一个非常常见的用例并且没有找到任何答案。我怀疑情况并非如此,因为在 repl 中键入“Transforms/floor”会将其视为静态字段而不是方法,但我不确定。

标签: clojurestatic-methods

解决方案


您可以编写一个扩展为包含重复代码的函数定义的宏:

(defmacro defoperator [op]
  `(defn ~op ; Create a function with "op" as the name
     ^INDArray
     [^INDArray a#, dup#]
     (. Transforms ~op a# dup#))) ; (. Transforms floor) is the same as (Transforms/floor)

现在调用(defoperator floor)会发出一个函数定义,如下所示:

(defn floor
  ^INDArray
  [^INDArray a, dup]
  (. Transforms floor a dup))

此宏假定静态方法名称与您要发出的函数相同。如果您希望它们可能有所不同,您可以使用替代版本:

(defmacro defoperator [fn-name static-name]
  `(defn ~fn-name
     ^INDArray
     [^INDArray a#, dup#]
     (. Transforms ~static-name a# dup#)))

注意,我摆脱了你的^boolean暗示。你不能暗示布尔原语。我不确定您使用的是哪个版本的 Clojure,但这在 1.8.0 中不起作用。


推荐阅读