Как принимать в спецификации только заказанные коллекции

Как я могу создать спецификацию, которая принимает только последовательные (то есть сохраняющие порядок) коллекции?

Например

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 Я перепутал последовательное/упорядоченное различие в исходном вопросе. Почистил терминологию.


person CheatEx    schedule 13.12.2018    source источник
comment
+ означает 1..n (как в регулярном выражении). Это не предполагает никакого порядка. Вам нужно будет добавить предикат для этого самостоятельно.   -  person cfrick    schedule 13.12.2018


Ответы (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?.

person glts    schedule 13.12.2018

Как я могу сделать спецификацию, которая принимает только коллекции, сохраняющие порядок?

Существует функция предиката 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
person Taylor Wood    schedule 13.12.2018
comment
Упорядочено не значит отсортировано… - person glts; 13.12.2018
comment
Я использовал плохие формулировки. Под упорядоченным я имел в виду не сортированный, а сохраняющий порядок. - person CheatEx; 14.12.2018