Как принимать только заказанные коллекции в спецификации
Как я могу создать спецификацию, которая принимает только последовательные (т.е. сохраняющие порядок) коллекции?
Например
cljs.user=> (s/def ::path (s/+ number?))
:cljs.user/path
cljs.user=> (s/explain ::path [])
val: () fails spec: :cljs.user/path predicate: number?, Insufficient input
:cljs.spec.alpha/spec :cljs.user/path
:cljs.spec.alpha/value []
cljs.user=> (s/explain ::path [1 2 3])
Success!
Это как и ожидалось, но в то же время обратите внимание на порядок
cljs.user=> #{1 2 3}
#{1 3 2}
cljs.user=> (s/explain ::path #{1 2 3})
Success!
И это не имеет никакого смысла. Итак, вторичный вопрос:
Почему выражения, относящиеся к последовательности (cat, *, +,?) В спецификации, принимают коллекции, нарушающие последовательность?
UPD Я перепутал последовательное / упорядоченное различие в оригинальном вопросе. Вычистил терминологию.
2 ответа
Спецификации для последовательностей (спецификации регулярных выражений) не должны соответствовать упорядоченным, то есть последовательным коллекциям. Эта ошибка была исправлена в текущих версиях спецификации, см. CLJ-2183.
В Clojure 1.10.0-RC5 результаты ожидаемые:
(s/conform ::path [1 2 3]) ; => [1 2 3]
(s/conform ::path #{1 2 3}) ; => :clojure.spec.alpha/invalid
(s/explain ::path #{1 2 3})
;; #{1 3 2} - failed: (or (nil? %) (sequential? %)) spec: :user/path
В последней строке видно, что спецификации регулярных выражений теперь соответствуют только значениям, которые sequential?
,
Как я могу создать спецификацию, которая принимает только сохраняющие порядок коллекции?
Есть функция предиката clojure.core sorted?
который вернет true для коллекций, которые реализуют Sorted
,
(sorted? (sorted-map))
=> true
Он не возвращает true для коллекций, содержимое которых отсортировано, но не реализовано Sorted
:
(sorted? [1 2 3])
=> false
Вы можете использовать произвольные функции предикатов в спецификациях, чтобы вы могли определить функцию, которая возвращает true для коллекций с отсортированным содержимым:
(defn ordered? [coll]
(or (empty? coll) (apply <= coll)))
(ordered? [1 2 3])
=> true
Тогда вы можете использовать s/and
объединить этот предикат с вашей спецификацией регулярных выражений:
(s/def ::path (s/and (s/+ number?)
ordered?))
(s/explain ::path [1 2 3])
Success!
=> nil
(s/explain ::path #{1 2 3})
val: [1 3 2] fails spec: :playground.so/path predicate: ordered?
=> nil