是否期望身份返回与其论点不同的东西?

时间:2017-06-27 19:58:03

标签: clojure nan equality

这是一个调用identity更改返回值的示例,在我看来,这表明文档字符串"返回其参数。"并非完全正确:

(let [x Double/NaN] (identical? x x)) ;=> false
(let [x (identity Double/NaN)] (identical? x x)) ;=> true

这是预期的吗?或者它是identity函数的错误?

3 个答案:

答案 0 :(得分:8)

您似乎找到了涉及identityidentical?以及原始与对象相等的边缘情况。请注意,在Java中,java.lang.Double/NaN is a primitive:

public static final double NaN

但是相同的比较Java对象:

; clojure.core
(defn identical?
  "Tests if 2 arguments are the same object"
  {:inline (fn [x y] `(. clojure.lang.Util identical ~x ~y))
   :inline-arities #{2}
   :added "1.0"}
  ([x y] (clojure.lang.Util/identical x y)))

// clojure/lang/Util.java
static public boolean identical(Object k1, Object k2){
    return k1 == k2;
}

尝试使用此技巧将NaN强制转换为Double对象而不是未装箱的原语:

tupelo.core=> (let [x (Double. Double/NaN)] 
  (spyxx x) 
  (identical? x x))

x => java.lang.Double->NaN
true

我怀疑原始NaN的自动装箱可能会/可能不会在不同的用例中出现,这是您所看到的差异的原因。

答案 1 :(得分:3)

为艾伦关于拳击的答案添加一点颜色:

您可能需要查看==函数,该函数以这种方式实现:

public boolean equiv(Number x, Number y){
    return x.doubleValue() == y.doubleValue();
}

这将执行两个实际double的原始比较。您的示例,==

(let [x (identity Double/NaN)] (== x x))
=> false
(let [x (identity Double/POSITIVE_INFINITY)] (== x x))
=> true

发生了什么?为什么NaN == NaN是假的?好吧,使用== 的原始比较实际上应该为NaN 返回false。它在IEEE 754中以这种方式奇怪地指定并且Java以这种方式运行。它是唯一的"数字"与自身相比,它不等于自己。

顺便说一句,要了解对象相等在Java中是多么奇怪,请参阅:

(identical? 127 127)
=> true
(identical? 128 128)
=> false

这是因为java缓存了前2 ^ 8个无符号整数,因此被比较的127在第一个示例中是相同的对象,但第二个示例中的128是不同的对象。因此,检查平等时需要注意一些问题!

但这里的主要内容是:identity正常运作!在比较事物时要小心,因为"平等"不是那么简单!

答案 2 :(得分:0)

identity “返回其参数”吗?

这取决于参数的含义。

  • 如果是函数调用表单中的计算表达式,那么并不总是
  • 如果该函数的主体在进入时在堆栈上看到的内容,则是,它确实

由于Clojure调用函数的方式而产生异常。

  • Clojure函数是符合the IFn interface的对象。
  • Clojure函数调用转换为众多invoke之一 方法 - 为arity重载 - 函数对象。
  • 所有invoke方法都有Object个参数。

所有这一切的结果是每个Clojure函数调用都会将其每个参数转换为某种Object - 一个身份操作,除了原语之外,它们包含在相应的Java类中:longLong,等等。

因此即使identity函数(实质上是(defn identity [x] x))也不返回原始参数。它不能,因为它永远不会看到它。

例如,让我们考虑表达式

(inc 3)

数字3肯定是long。什么类型(inc 3)?我们问Clojure:

(type (inc 3))
=> java.lang.Long

... 盒装 Long对象。

坚持,我们确定3是原始long?:

(type 3)
=> java.lang.Long

Aaaaaaagh!它也装箱了!

不一定!您无法分辨,因为当type的主体看到3时,它的框,无论是否对读者/编译器都是如此。 Clojure documentation在这一点上保持沉默。它只是说数字文字通常用Java 表示。

所以 - 通常 - 它是评估机制,而不是负责装箱原始参数的特定功能(例如identity。这是自动拳击。

你的例子显示原语是这样的,没有盒子,至少以let形式存在:

(let [x 1.0] (identical? x x)) ;=> false
(let [x (identity 1.0)] (identical? x x)) ;=> true

identical?能够区分1.0这两个标记的事实表明它被保存为原始double。 (我使用了普通的double,只是为了表明行为与特殊值Double/NaN无关。

现在让我们尝试将数字放在var:

(def x 1.0)

(identical? x x) ;=> true
(let [x (identity x)] (identical? x x)) ;=> true

它是盒装的。

虽然我们在这里,但自动装箱是幂等的:

(identical? x (identity x)) ;=> true

上述内容对Alan Thompson'sJosh's答案以及Alan Malloy和Lee的评论所构成的内容几乎没有什么影响。我只觉得他们已经迷上了鱼并且没有实际登陆它。