2017-01-27 10 views
4

Ниже Clojure спецификации ::my разрешения карты, имеющие либо ключ: ширину или ключа: высота, однако он не допускает имеющие обе из них:Clojure спецификации: карта, содержащая либо: с или: высота (исключающее ИЛИ)

(s/def ::width int?) 

(s/def ::height int?) 

(defn one-of-both? [a b] 
    (or (and a (not b)) 
     (and b (not a)))) 

(s/def ::my (s/and (s/keys :opt-un [::width ::height]) 
        #(one-of-both? (% :width) (% :height)))) 

Даже если он делает работу:

(s/valid? ::my {}) 
false 
(s/valid? ::my {:width 5}) 
true 
(s/valid? ::my {:height 2}) 
true 
(s/valid? ::my {:width 5 :height 2}) 
false 

код не кажется, что лаконичный мне. Сначала ключи определяются как необязательные, а затем по необходимости. У кого-нибудь есть более читаемое решение?

+0

Просто хотелось бы отметить, что вышеуказанная логика терпит неудачу, если значение принадлежит каким-либо из клавиш false, т.е. 'false' или' nil': '(spec/valid? :: my {: width nil})' => 'false'. – Rovanion

ответ

5

clojure.spec предназначен для поощрения спецификаций, способных к росту. Таким образом, его s/keys не поддерживает запрещающие ключи. Он даже сопоставляет карты с ключами, которые не находятся ни в :req, ни в opt.

Существует, однако, способ сказать карту по крайней мере должны :widthили:height, т.е. не XOR, просто ИЛИ.

(s/def ::my (s/keys :req-un [(or ::width ::height)])) 
+0

в порядке, получил смысл. –

3

Эта функция встроена в спецификации - вы можете указать и/или узоры в REQ Уна:

(s/def ::my (s/keys :req-un [(or ::width ::height)])) 
:user/my 
user=> (s/valid? ::my {}) 
false 
user=> (s/valid? ::my {:width 5}) 
true 
user=> (s/valid? ::my {:height 2}) 
true 
user=> (s/valid? ::my {:width 5 :height 2}) 
true 
0

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

(spec/valid? ::my {:width nil}) 
=> false 

Конечно, это не будет происходить при заданных ограничениях int? положить на значения в вопросе. Но, возможно, кто-то из потомков позволяет их значениям быть ниразмерными или логическими, и в этом случае этот ответ становится удобным.

Если мы вместо того, чтобы определить спецификацию как:

(defn xor? [coll a-key b-key] 
    (let [a (contains? coll a-key) 
     b (contains? coll b-key)] 
    (or (and a (not b)) 
     (and b (not a))))) 

(spec/def ::my (spec/and (spec/keys :opt-un [::width ::height]) 
         #(xor? % :width :height))) 

мы получим результат, который

(spec/valid? ::my {:width nil}) 
=> true 
(spec/valid? ::my {:width nil :height 5}) 
=> false 

 Смежные вопросы

  • Нет связанных вопросов^_^