7

观察以下 repl 会话:

user=> (set! *warn-on-reflection* true)
true

user=> (defn blah [s] (for [c s] (if (Character/isDigit c) true false)))
Reflection warning, NO_SOURCE_PATH:1:31 - call to isDigit can't be resolved.
Reflection warning, NO_SOURCE_PATH:1:31 - call to isDigit can't be resolved.
#'user/blah

user=> (blah "abc123abc")
(false false false true true true false false false)

user=> (defn blah [s] (for [^char c s] (if (Character/isDigit c) true false)))
#'user/blah

user=> (blah "abc123abc")
(false false false true true true false false false)

所以我们使用了一个类型提示^char来摆脱反射——太好了。现在在函数参数中尝试同样的事情:

user=> (defn blah-c [c] (if (Character/isDigit c) true false))
Reflection warning, NO_SOURCE_PATH:1:22 - call to isDigit can't be resolved.
#'user/blah-c

user=> (defn blah-c [^char c] (if (Character/isDigit c) true false))
CompilerException java.lang.IllegalArgumentException: Only long and double primitives are supported, compiling:(NO_SOURCE_PATH:1:1) 

user=> (defn blah-c [^Character c] (if (Character/isDigit c) true false))
#'user/blah-c
user=> (blah-c \1)
true
user=> (blah-c \a)
false

我了解 Clojure仅支持数字原语的长或双精度类型提示,并且 Javachar是数字数据类型 - 无需解释。但上面似乎不一致的类型提示^char在第一个函数中是允许的for,但不是在函数签名中blah-c,我必须在其中指定Character。这是什么原因(即从编译器实现的角度来看)?

4

2 回答 2

3

在类型提示for表达式中,您将ca 标记为char编译器的提示。当编译器为它发出(静态)方法时,isDigit就会知道您想要接受 a 的版本char(而不是可能的int版本)。字节码被发送到实现接口的O(单Object参数)版本的函数对象中(IFn默认情况下,所有参数都被装箱)。

在另一种情况下,blah-c需要将字节码发送到实现接口的不存在C(例如,for char)版本的函数对象IFn。每个原语都可以有接口吗?当然,但没有。对于每个可能的组合?由于组合爆炸,不可行。

你可以说,好吧,为什么不直接发射blah-c到一个O接口呢?这将破坏函数参数上的类型提示点,即避免装箱/拆箱,因为必须对字符原语进行装箱才能进行调用。函数参数的类型提示不仅仅是为了避免反射。如果您想在这里避免反射,那么您不会标记函数参数,而是在进行调用之前将其强制转换为块char中的 a 。letisDigit

请注意,在clojure.lang.IFn中,枚举接口(当前)仅限于任意数量的对象(盒装类型)和最多四种doublelong类型的组合。double和版本是作为一种优化来提供的long,以避免在对原语编写性能关键代码时避免装箱/拆箱,并且对于大多数用途来说应该足够了。

于 2013-06-27T20:55:50.943 回答
0

这是基于@A 的评论。韦伯和@kotarak,据我所知。

这有两个方面:首先,为什么^char在某些情况下(例如在for)中可用?正如您的示例所示,这不仅对于优化是必要的,而且对于正确的 Java 互操作也是必要的。此外,它(在我看来)实现起来相对便宜,因为每个变量都是独立的,所以它可以自己处理。

这不是函数定义的情况,对于支持类型的每种组合,您必须定义一个新接口:例如

static public interface L{long invokePrim();}
static public interface D{double invokePrim();}
static public interface OL{long invokePrim(Object arg0);}
// ...
static public interface OLD{double invokePrim(Object arg0, long arg1);}
// all the way to
static public interface DDDDD{double invokePrim(double arg0, double arg1, double arg2, double arg3);}

每种新支持的类型都会添加许多新接口(呈指数增长)。这就是为什么只支持最广泛的原始类型:longdouble.

于 2013-06-27T13:16:40.753 回答