2013-12-01 5 views
0

Я новичок в clojure и пытаюсь сравнить список символов, и я столкнулся с каким-то запутанным поведением. Почему трудно (невозможно?) Сравнивать равенство списка символов, когда сравнивать сравнение строки с конкатенированной строкой просто?Проверка равенства символов в clojure

(identical? (\A \T \C \G) (\A \T \C \G)) 
; ClassCastException java.lang.Character cannot be cast to clojure.lang.IFn user/eval672  
;(NO_SOURCE_FILE:1) 
(identical? '(\A \T \C \G) '(\A \T \C \G)) 
;false 

;convert to string 
(identical? "ATCG" "ATCG") 
;True 

ответ

10

От РЕПЛ:

user=> (doc identical?) 
------------------------- 
clojure.core/identical? 
([x y]) 
Tests if 2 arguments are the same object 

Если вы знаете язык программирования Java, а затем идентичны? ведет себя как == оператор в Java, когда имеет дело со ссылками.

Вы можете попробовать это:

(= '(\A \T \C \G) '(\A \T \C \G)) 
=> true 

Опять же, в REPL:

user=> (doc =) 
------------------------- 
clojure.core/= 
([x] [x y] [x y & more]) 
Equality. Returns true if x equals y, false if not. Same as 
Java x.equals(y) except it also works for nil, and compares 
numbers and collections in a type-independent manner. Clojure's immutable data 
structures define equals() (and thus =) as a value, not an identity, 
comparison. 

Так что нет, это не невозможно, и, безусловно, не трудно сравнивать равенство списков в Clojure. REPL - ваш лучший друг.

+0

спасибо за быстрый ответ. (не могу поверить, что я не пробовал '=' first ....) – zach

+0

Еще одно небольшое добавление от [this] (http://dev.clojure.org/display/design/Documentation+for+1.3+Numerics) : Оператор = равенство (равенство) проверяет равенство. Он сравнивает значения не зависящим от типа образом, но не между плавающими точками и целыми типами. Это позволяет использовать числа в качестве ключей карты с правильной семантикой. Чтобы проверить числовую эквивалентность между типами с плавающей точкой и целым числом, используйте оператор == (эквивалентность). –

1

В качестве дополнения к @Chiron's answer: у вас есть три вида равенства в Clojure.

Clojure Равенство или Эквивалентность

=== для чисел) является специфичным для Clojure и тот, который вы будете использовать больше всего. Он выполняет независимое от типа сравнение значений, что означает, что числа той же категории (целые числа против десятичных знаков) и аналогичные структуры данных (например, списки, векторы и множества или карты и последовательности пар) могут быть равны в соответствии с этим определением. Он также работает с чистыми Java-типами.

(= 5 5N) ;; true 

(import 'java.util.ArrayList) 
(= '(:a :b) 
    (let [l (ArrayList.) 
     _ (.add l :a) 
     _ (.add l :b)] 
    l)) ;; true 

Java Равенство

Все типы Clojure являются Java-классы под капотом, поэтому все не нильполугруппы Clojure лица осуществлять .equals, и для большинства типов Clojure он ведет себя так же, как = бы. Но это меньше похожее на слепое сравнение; например, большинство типов Clojure - это чистые типы Java, а для всех числовых типов Java .equals зависит от типа.

(.equals 5 5N) ;; false 

Опасайтесь. There are many pitfalls in writing equality methods in Java. Многие разработчики библиотеки упали.

Идентичность объекта

identical? ведет себя так же, как == оператор Java, который возвращает true тогда и только тогда, когда оба параметра тот же экземпляр объекта; это равенство «адрес в памяти», поэтому более строгий инструмент доступен. Но иногда это именно то, что вам нужно.


Что касается поведения вы столкнулись:

  • (identical? '(\A \T \C \G) '(\A \T \C \G)) возвращает false, потому что два различных экземпляра списка создаются в скомпилированный код (то же самое произошло бы с вектором, карта и набор литералов); как следствие/контр-пример, это будет работать:

    (let [a '(A \T \C \G)] 
        (identical? a a)) 
    

    Компилятор не видит два список литералов как идентичные; он видит только два параметра и компилирует каждый из них в качестве нового списка, следовательно, два отдельных экземпляра списка. Однако их содержимое во время выполнения одинаково, поскольку эти символьные литералы кэшируются; поэтому являются булевы литералы и некоторые (но не все) числовые литералы:

    (identical? \A \A) 
    
    (identical? true true) 
    
    (identical? 5 5)    ;; Long 
    (identical? 0N 0N)   ;; BigInt 
    (identical? (byte 6) 
          (byte 6)) 
    (identical? (short 7) 
          (short 7)) 
    (identical? (int 8) 
          (int 8)) 
    (identical? (biginteger 9) 
          (biginteger 9)) 
    (identical? (bigdec 10) 
          (bigdec 10)) 
    

    Большинство кэши типа имеют ограничения, хотя; все следующие не кэшируются (и, таким образом, возвращает identical?false):

    ;; Java cache limitations 
    
    (identical? (char 128)  ;; Characters with codepoint outside of 0..127 
          (char 128))  ;; i.e. non-"C0 Control/Basic Latin" Characters 
    
    (identical? 128 128)   ;; Longs/Integers/Shorts outside of -128..127 
    (identical? 0. 0.)   ;; Doubles and Floats are not cached 
    (identical? (biginteger 17) 
          (biginteger 17)) ;; BigIntegers outside of -16..16 
    (identical? (bigdec 11) 
          (bigdec 11))  ;; BigDecimals outside of 0..10 
    
    ;; Clojure cache limitations 
    
    (identical? 1N 1N)   ;; 0N is the only cached BigInt literal 
    (identical? 0M 0M)   ;; BigDecimal literals are not cached 
    (identical? 1/2 1/2)   ;; Ratios are not cached 
              ;; Note that Ratio literals of the form X/1 
              ;; and any other reducible to an integer 
              ;; e.g. 10/10 are compiled as integer types 
              ;; (Long/BigInt) 
    
  • (identical? "ATCG" "ATCG") возвращает true, потому что Clojure String литералы интернирован; поэтому ключевые слова:

    (identical? :foo :foo) 
    
    ;; but not symbol literals 
    (identical? 'foo 'foo) ;; false 
    
Смежные вопросы