11

Can you explain with examples how does retag parameter impacts multi-spec creation? I find multi-spec documentation hard to digest.

4

2 回答 2

6

从文档字符串:

retag 在生成期间使用匹配标签重新标记生成的值。retag 可以是关键字,dispatch-tag 将在哪个键上关联,或者是生成值和 dispatch-tag 的 fn,它应该返回适​​当的重新标记值。

Ifretag是一个关键字(如规范指南示例中),在此处multi-spec内部创建一个函数,用于生成器实现函数。例如,这两个多规范声明在功能上是等效的:

(s/def :event/event (s/multi-spec event-type :event/type))
(s/def :event/event (s/multi-spec event-type
                                  (fn [genv tag]
                                    (assoc genv :event/type tag))))

鉴于指南的示例,传递retag 函数似乎不是一个非常有用的选项,但在multi-spec用于非地图时更有用。例如,如果您想使用multi-specwith s/categ 来指定函数参数:

(defmulti foo first)
(defmethod foo :so/one [_]
  (s/cat :typ #{:so/one} :num number?))
(defmethod foo :so/range [_]
  (s/cat :typ #{:so/range} :lo number? :hi number?))

foo需要两个或三个参数,具体取决于第一个参数。如果我们天真地尝试multi-spec使用s/cat关键字/标签,它将不起作用:

(s/def :so/foo (s/multi-spec foo :typ))
(sgen/sample (s/gen :so/foo))
;; ClassCastException clojure.lang.LazySeq cannot be cast to clojure.lang.Associative

这是能够传递retag函数的用处:

(s/def :so/foo (s/multi-spec foo (fn [genv _tag] genv)))
(sgen/sample (s/gen :so/foo))
;=>
;((:so/one -0.5)
; (:so/one -0.5)
; (:so/range -1 -2.0)
; (:so/one -1)
; (:so/one 2.0)
; (:so/range 1.875 -4)
; (:so/one -1)
; (:so/one 2.0)
; (:so/range 0 3)
; (:so/one 0.8125))
于 2017-10-09T15:30:43.913 回答
1

我同意文档很简洁!

我想生成一个multi-spec带有可以有多个值的标签的 d 映射。我发现传递给retag函数的第二个参数实际上是调度标签,而不是分配的标签(就像文档说的那样,回想起来)。这导致s/gen生成仅使用(非默认)多方法调度选项标记的地图,而不是标签规范涵盖的全部范围。

(s/def ::tag #{:a :b :c :d})
(s/def ::example-key keyword?)
(s/def ::different-key keyword?)

(defmulti tagmm :tag)
(defmethod tagmm :a [_]
  (s/keys :req-un [::tag ::example-key]))
(defmethod tagmm :default [_] ; this is `defmulti`'s :default
  (s/keys :req-un [::tag ::different-key]))

(s/def ::example (s/multi-spec tagmm :tag))

(gen/sample (s/gen ::example))
;=> only gives examples with {:tag :a, ...}

提供一个retag忽略其第二个参数并返回生成值的 a 使生成器按预期工作。

(s/def ::example (s/multi-spec tagmm (fn [gen-v tag] gen-v)))
;=> now gives examples from every ::tag

很难锻炼,但值得!

于 2018-08-13T12:42:53.317 回答