有效地将 Clojure 函数传递给 java

Pass Clojure functions to java efficiently

我想在 Java 中使用 Java 循环实现一个朴素的非惰性 map。 我主要关心的是 Clojure java 中的函数调用。

这是我的代码:

A class 调用 NaiveClojure 使用 Java

实现功能
package java_utils;

import java_utils.ApplyFn;

public class NaiveClojure {

    public static Object[] map (ApplyFn applyfn, Object function, Object[] coll) {

        int len = coll.length;

        for (int i = 0 ; i < len ; i++) {
            coll[i] = applyfn.apply(function, coll[i]);
        }

        return coll;
    }
}

名为 ApplyFn 的摘要 class

package java_utils;

public abstract class ApplyFn {

  public abstract Object apply (Object function, Object value);

}

所以在 Clojure 中我有

(defn java-map [f coll]
  (let [java-fn (proxy [ApplyFn] []
                  (apply [f x]
                         (f x)))]
    (seq (NaiveClojure/map java-fn f (to-array coll)))))

我试过了

(doall (map inc (range 0 10000))) ;; 3.4 seconds for 10000 operations
(java-map inc (range 0 10000) ;; 5.4 seconds

我的意思不是要超越map(我是作为例子实现的),我只是想用特定的功能做那样的事情(而不是重新发明Clojure的轮子)。

有没有更好的方法来传递这样的函数? (作为一种简单快捷的方式) 为了总体上改进我的编码(我的理论知识很差),你知道这里的 perf 是什么吗? 我会说像 Object 这样的一般类型,但我没有看到其他任何东西

谢谢

你不用担心,你的做法很好,也很高效。

coll[i] = applyfn.apply(function, coll[i]);

这是一种非常正常的处理方式。当测量它时,请记住,正如 Valentin Waeselynck 指出的那样,记住使用可靠的微基准测试功能,还要记住,单独对这种小代码块进行基准测试是很棘手的。

当您生成一个 clojure 函数时,它会生成一个 "normal" java class,并带有一个名为 apply 的方法。调用起来不会慢,因为您正在调用一个最初用 Clojure 编写的函数,而不是在 class 上调用一个用普通 Java 语法编写的方法。一旦 Hotspot JIT 完成预热和内联,它可能会像没有方法调用一样快(这就是为什么对这种事情进行基准测试比直觉上应该更难)。