是否期望 identity returns 与其参数有所不同?

Is it expected that identity returns something different from its argument?

这是一个调用 identity 更改返回值的示例,在我看来这表明文档字符串 "Returns its argument." 不完全正确:

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

这是预期的吗?还是 identity 函数的错误?

您似乎发现了涉及 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 的自动装箱 may/may 不会出现在不同的用例中,这是造成您所看到的差异的原因。

为 Alan 关于拳击的回答增添一点色彩:

您可能需要查看 == 函数,它是这样实现的:

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 应该 return 为假。它在 IEEE 754 中以这种方式指定,而 Java 以这种方式表现。它是唯一的 "number",与自身相比,它不等于自身。

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

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

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

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

identity"return its argument"吗?

这取决于您所说的 参数 的意思。

  • 如果是函数调用形式中的求值表达式,则不总是
  • 如果它是函数体在进入时在堆栈上看到的,那么是的,它是

异常的产生是因为Clojure调用函数的方式。

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

所有这一切的结果是,每个 Clojure 函数调用都将其每个参数转换为某种类型的 Object - 除了原语之外的身份操作,它们被包装在相应的 Java class:long变成Long,依此类推。

因此,即使是 identity 函数,本质上 (defn identity [x] x),也不是 return 原始参数。它不能,因为它从来没有看到它。


例如,让我们考虑表达式

(inc 3)

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

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

... 盒装 Long 对象。

等一下,我们确定 3 是原始 long 吗?:

(type 3)
=> java.lang.Long

啊啊啊啊啊!也是盒装的!

不一定!你不知道,因为当 type 的主体看到 3 时,它 装箱的,无论 [=120 是否如此=]. 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 无关)。

现在让我们尝试将数字放入变量中:

(def x 1.0)

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

它是盒装的。

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

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

以上内容对 and 的回答以及 Alan Malloy 和 Lee 的评论所包含的内容没有什么补充。我只是觉得他们只是钩住了鱼,并没有真正把鱼钓上来。