#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)))