【问题标题】:What does retag parameter in s/multi-spec mean?s/multi-spec 中的 retag 参数是什么意思?
【发布时间】:2018-01-05 18:23:12
【问题描述】:

您能否举例说明retag 参数如何影响multi-spec 创建?我发现 multi-spec 文档很难消化。

【问题讨论】:

    标签: clojure multimethod clojure.spec


    【解决方案1】:

    来自文档字符串:

    retag 在生成过程中用于重新标记生成的值 匹配的标签。 retag 可以是关键字,在哪个关键字 dispatch-tag 将被关联,或生成值的 fn 和 dispatch-tag 应该返回一个适当的重新标记的值。

    如果retag 是关键字(如spec guide example),multi-spec 在内部创建一个函数here,用于生成器实现函数。例如,这两个多规范声明在功能上是等效的:

    (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-specs/cat 一起使用,例如指定函数参数:

    (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 接受两个或三个参数,具体取决于第一个参数。如果我们尝试使用s/cat 关键字/标签天真地multi-spec 这个,它将不起作用:

    (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))
    

    【讨论】:

    • 感谢您的精彩解释和示例!
    【解决方案2】:

    我同意文档很简洁!

    我想生成一个multi-specd 映射,其标签可以有多个值。我发现传递给 retag 函数的第二个参数实际上是 dispatch 标记,而不是分配的标记(就像文档所说,回想起来)。这导致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,它只是忽略了它的第二个参数并返回了生成的值,使生成器按预期工作。

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

    很难锻炼,但值得!

    【讨论】:

    • 我认为如果你有(s/def ::tag keyword?),那么你可以有(s/def ::example (s/multi-spec tagmm ::tag))(注意限定关键字!)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-15
    • 2020-06-06
    • 1970-01-01
    • 2022-06-10
    • 2011-09-27
    • 1970-01-01
    相关资源
    最近更新 更多