Как проверить / отфильтровать новые элементы для добавления (добавления) в коллекцию в clojure

#validation #collections #clojure

#проверка #Коллекции #clojure

Вопрос:

Мне нужно предоставить правило проверки для добавления / добавления нового элемента в коллекцию. Это правило будет конкретным сравнением с элементами, уже существующими в коллекции.

Например, если моя коллекция представляет собой сортированный набор, мне нужны не только уникальные элементы, которые будут приняты conj, но и они должны быть уникальными в каком-то другом конкретном случае 🙂

Мой конкретный случай заключается в следующем (не критикуйте мой пример:) это далеко от реальности):

У меня есть набор хребтов («цепочки взглядов в горах») в качестве векторов их «взглядов» в разное время 🙂

Например.

много-много лет назад в горах был один хребет: #{[1 0 2 0]}

много лет назад вырос еще один хребет: #{[1 0 2 0] [2 0 1 0]}

И теперь… Я хочу разрешить добавлять только ребра, приемлемые по следующему правилу:

  • допустимый гребень должен иметь по крайней мере один взгляд выше, чем хотя бы один взгляд другого гребня на ту же «позицию в его векторе».

Например, в нашем последнем случае #{[1 0 2 0][2 0 1 0]} ,

Допустимыми ребрами являются: [0 0 0 1][0 1 0 0][0 1 0 1][3 0 1 0] [1 0 3 0] [1 1 2 0] [2 0 1 1] [2 1 1 0] , и т.д…

Недопустимыми ребрами являются: [1 0 1 0] [0 0 1 0] , и т.д…

Короче говоря, мы принимаем только ребра с «хотя бы в каком-то месте» более высокими значениями 🙂

Вопрос в том,
какой лучший способ реализовать такую проверку в clojure?

  • расширить коллекцию (set, sorted-set) для проверки в дополнение к уникальности также «моя странная уникальность? Как это сделать?
  • реализовать мою собственную коллекцию?
  • просто каждый раз, прежде чем переходить к коллекции, используйте if функцию проверки с помощью?
  • использовать какую-либо доступную функцию clojure? Например, вы можете добавить пользовательский компаратор в sorted-set-by. Вопрос в том, возможно ли предоставить какое-либо пользовательское «правило проверки»? Существуют ли какие-либо надлежащие библиотеки clojure?
  • есть еще идеи?

Ответ №1:

Создание нового типа коллекции немного утомительно, но является (правильным?) правильным решением (не забудьте реализовать чтение / печать, могут помочь литералы тегов).

Выполнение if перед каждым conj из них неоптимально, поскольку для повышения эффективности вам может потребоваться сохранить некоторое свойство в коллекции: в вашем примере вы можете сохранить минимальную высоту для каждой позиции в векторе (что позволяет решить, добавлять ли элемент в O (1) вместо O (n)).

Самое меньшее, что вы можете сделать, это conj-ridge функция:

 (defn conj-ridge [[ridges min-heights :as ridges-set] ridge]
  (if (= (map max min-heights ridge) min-heights)
    ridges-set
    [(conj ridges ridges) (map min min-heights ridge)])) 
  

Ответ №2:

Вы могли бы использовать предварительное условие:

 (defn higher-peak? [old-ridge new-ridge]
  (some #(> (new-ridge %) (old-ridge %)) 
        (range (count old-ridge))))

(defn conj-ridge [ridges ridge]
  {:pre [(some #(higher-peak? % ridge) ridges)]}
  (conj ridges ridge))
  

Это приведет к возникновению ошибки утверждения всякий раз, когда вы пытаетесь использовать conj-ridge для добавления гребня, у которого нет более высокого пика. Если вы не хотите ошибки, вы можете написать функцию более высокого порядка, которая переносит вызов conj-ridge в try / catch block:

 (defn conj-ridge-if-valid [ridges ridge]
  (try 
    (conj-ridge ridges ridge)
    (catch AssertionError e ridges)))