2017-06-27 17 views
5

下面是一個例子,其中調用identity更改返回的值,這似乎表明文檔字符串「返回其參數」。並非完全正確:預計身份是否會返回與其論點不同的東西?

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

這是預期嗎?或者它與某種程度上的identity功能有關嗎?

+4

這可能是由於拳擊 - 「Double/NaN」返回一個原始的'double',在第一個例子中它被裝箱兩次,但在第二個例子中只有一次。 – Lee

回答

8

您似乎發現了一個涉及identity,identical?以及原語與對象相等的邊界情況。需要注意的是在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的自動裝箱可能會/不會出現在不同的使用情況下,這是您所看到的差異的原因。

+0

最後的訣竅與這個問題提出的問題是一樣的:你把NaN放在Double中,就像問題中的身份一樣。這不是一回事。我認爲這個答案缺失的唯一原因是「身份」和「相同?」爲什麼需要自動裝箱。 – amalloy

3

要一點點顏色添加到艾倫的回答拳擊:

你可能想看看進入==功能,這是這種方式實現:

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

執行對兩個實際原始的比較double s。你的例子,與==

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

這是怎麼回事?爲什麼NaN == NaN是假的?那麼,使用==的原始比較實際上應該返回假爲NaN。在IEEE 754中奇怪地指定了這種方式,Java的行爲就是這樣。這是唯一的「數字」,與其本身相比,並不等同。

順便說一句,看物體平等如何能夠在Java中一個奇怪的東西,看到這一點:

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

這是因爲Java緩存第2^8個無符號整數,所以被比較的127小號在第一個例子中是相同的對象,但第二個例子中的128是不同的對象。所以,在檢查平等時需要注意一些問題!

但這裏的主要外賣是:identity正在工作,因爲它應該!在比較事物時要小心,因爲「平等」的概念並不那麼簡單!

+0

這並沒有回答這個問題,這個問題是關於返回值彼此不同的事實,而不是關於返回值具體是什麼。 –

0

是否identity「返回其參數」

這取決於你的意思論據

  • 如果是在函數調用形式所評估的表達式,然後並不總是
  • 如果它是函數的主體在入口處看到堆棧的內容,那麼是的,它確實是

由於Clojure調用函數的方式而出現異常。

  • Clojure功能是符合the IFn interface的對象。
  • Clojure函數調用轉換爲函數對象的許多方法之一 - 重載爲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兩個boxings區分表明,保持爲原始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 

上面增加了小到什麼Alan Thompson'sJosh's答案和艾倫馬洛伊的和李的意見包括。我只是覺得他們已經迷上了魚,沒有真正落地。

相關問題