Сравнение вложенных структур данных, содержащих потенциально пустые массивы примитивов, с clojure.data/diff

Я использую clojure.data/diff для сравнения вложенных структур данных в своих модульных тестах. Он работал нормально, пока я не столкнулся с проблемой, что он (IMO) ведет себя непоследовательно при встрече с пустыми массивами примитивов.

Непустые массивы примитивов отлично сравниваются с векторами, содержащими объекты того же типа (например, двойники). Однако пустые примитивные массивы не сравниваются равными (в смысле функции diff) с пустым вектором или даже с другим пустым массивом того же типа.

Вот сеанс repl, показывающий мою проблему. Я добавил некоторые комментарии.

nerom.nsd.dbserver=> (require '[clojure.data :as cd])
nil
;; this is as I would expect - a vector and a primitive array with 
;; same contents compare equal
nerom.nsd.dbserver=> (cd/diff [[1.1 2.2]] [(double-array [1.1 2.2])])
[nil nil [[1.1 2.2]]]
;; this is inconsistent with the previous - empty double array does 
;; not compare equal to an empty vector
nerom.nsd.dbserver=> (cd/diff [[]] [(double-array [])])
[[nil] [nil] nil]
;; two double arrays with the same contents compare equal
nerom.nsd.dbserver=> (cd/diff [(double-array [1.1 2.2])] [(double-array [1.1 2.2])])
[nil nil [[1.1 2.2]]]
;; except when they are empty, which is IMO inconsistent
nerom.nsd.dbserver=> (cd/diff [(double-array [])] [(double-array [])])
[[nil] [nil] nil]

Что я могу сделать, чтобы пустые массивы сравнивались с пустым вектором или, по крайней мере, с пустым вектором того же типа?


person Antti Karanta    schedule 13.10.2015    source источник


Ответы (1)


Кажется, я нашел подсказку:

если вы посмотрите на исходный код diff, вы увидите следующее:

(if (= a b)
    [nil nil a]
    (if (= (equality-partition a) (equality-partition b))
      (diff-similar a b)
      (atom-diff a b)))

два значения сначала сравниваются с equality-partition , определенным в протоколе EqualityPartition, который возвращает некоторый класс равенства ключевых слов

поэтому, если вы расширите этот протокол до примитивных массивов, они не будут считаться равными:

(require '[clojure.data :as d])

до:

user> (d/diff [(double-array [])] [(int-array [])])
[[nil] [nil] nil]

а затем вы расширяете протокол:

(extend-protocol d/EqualityPartition
  (Class/forName "[D")
  (equality-partition [_] :double-array))

(extend-protocol d/EqualityPartition
  (Class/forName "[I")
  (equality-partition [_] :int-array))

user> (d/equality-partition (double-array []))
:double-array
user> (d/equality-partition (int-array []))
:int-array

после:

user> (d/diff [(double-array [])] [(int-array [])])
[[#object["[D" 0x1e2f0f97 "[D@1e2f0f97"]] [#object["[I" 0x207c60b5 "[I@207c60b5"]] nil]

а однотипные массивы будут сравниваться по-прежнему

user> (d/diff [(double-array [])] [(double-array [])])
[[nil] [nil] nil]

user> (d/diff [(double-array [1])] [(double-array [10])])
[[[1.0]] [[10.0]] nil]

так что смотрите дальше в этом направлении: вы можете настроить сравнение, расширяющее этот протокол (и другие протоколы из clojure.data)

person leetwinski    schedule 13.10.2015
comment
Спасибо! Я думаю, что это позволяет мне выполнять сравнения так, как я хочу (например, сравнивать пустой двойной массив с пустым вектором). Я уже выпутался из футляра, который у меня под рукой, но думаю, что скоро мне понадобится ваша идея. - person Antti Karanta; 14.10.2015