Clojure.spec: `alt` vs` или `для спецификации последовательности
Я следую clojure.spec guide (http://clojure.org/guides/spec). Меня смущает разница между alt
и or
для спецификации последовательности.
Для меня два следующих примера работают одинаково хорошо. Итак, какая разница между этими двумя?
; Use `alt`
(s/def ::config (s/* (s/cat :prop string?
:val (s/alt :s string? :b boolean?))))
(s/explain ::config ["-server" "foo" "-verbose" true "-user" 13])
; Use `or`
(s/def ::config (s/* (s/cat :prop string?
:val (s/or :s string? :b boolean?))))
(s/explain ::config ["-server" "foo" "-verbose" true "-user" 13])
Ответы
Ответ 1
s/alt
предназначен для конкатенации вложенных спецификаций регулярных выражений, где использование s/or
указывает подпоследовательность. В вашем примере это не имеет значения, поскольку вы не используете вложенные спецификации регулярных выражений. Вот пример:
(s/def ::number-regex (s/* number?))
(s/def ::or-example (s/cat :nums (s/or :numbers ::number-regex)))
(s/valid? ::or-example [1 2 3])
;;-> false
(s/valid? ::or-example [[1 2 3]])
;;-> true
Как вы можете видеть, or
указывает подпоследовательность, в которой запущен новый контекст регулярного выражения, тогда как alt
указывает противоположное:
(s/def ::alt-example (s/cat :nums (s/alt :numbers ::number-regex)))
(s/valid? ::alt-example [1 2 3])
;;-> true
(s/valid? ::alt-example [[1 2 3]])
;;-> false
Ответ 2
Из http://clojure.org/guides/spec, мы знаем
При объединении регулярных выражений они описывают одну последовательность.
Это означает, что если вы хотите использовать вложенные последовательности, вы должны сделать это следующим образом.
(s/def ::config (s/*
(s/cat :prop string?
:val (s/spec
(s/alt :s string? :b #(instance? Boolean %))))))
И тогда ваши данные выглядят так ( Уведомление скобки вокруг)
(s/explain ::config ["-server" ["foo"] "-verbose" [true] "-user" [13]])
Кроме того, если вы делаете (s/or).
(s/def ::config (s/* (s/cat :prop string?
:val (s/spec
(s/or :s string? :b #(instance? Boolean %))))))
ваши данные должны быть такими же, как у старого ( Уведомление нет скобок вокруг)
(s/explain ::config ["-server" "foo" "-verbose" true "-user" 13])
BTW, для не вложенных последовательностей. все еще есть небольшая разница между (s/alt) и (s/or):
;;; for (s/or)
(s/def ::name-or-id (s/or :name string?
:id int?))
(s/conform ::name-or-id 42) ;;=> [:id 42]
;;; for (s/alt)
(s/def ::name-or-id (s/alt :name string?
:id int?))
(s/conform ::name-or-id [42]) ;;=> [:id 42]