代码之家  ›  专栏  ›  技术社区  ›  ericky

Clojure规格的正确嵌套?

  •  0
  • ericky  · 技术社区  · 6 年前

    我认为在函数中嵌套规范的正确顺序可能有问题-特别是 s/with-gen s/or

    我有这个功能和规格:

    (defn set-gift-pair-in-gift-history [g-hist g-year g-pair]
      (if (nil? g-hist)
        [{:giver :none, :givee :none}]
        (assoc g-hist g-year g-pair)))
    (s/fdef set-gift-pair-in-gift-history
            :args (s/with-gen
                    (s/or :input-hist (s/and
                                        (s/cat :g-hist :unq/gift-history
                                               :g-year (s/and int? #(> % -1))
                                               :g-pair :unq/gift-pair)
                                        #(<= (:g-year %) (count (:g-hist %))))
                          :input-nil (s/and
                                       (s/cat :g-hist nil?
                                              :g-year (s/and int? #(> % -1))
                                              :g-pair :unq/gift-pair)
                                       #(<= (:g-year %) (count (:g-hist %)))))
                    #(gen/let [hist (s/gen :unq/gift-history)
                               year (gen/large-integer* {:min 0 :max (max 0 (dec (count hist)))})
                               pair (s/gen :unq/gift-pair)]
                       [hist year pair]))
            :ret :unq/gift-history)
    

    哪些测试正确:

    (stest/check `set-gift-pair-in-gift-history)
    =>
    ({:spec #object[clojure.spec.alpha$fspec_impl$reify__2451
                    0x729d93b6
                    "clojure.spec.alpha$fspec_impl$reify__2451@729d93b6"],
      :clojure.spec.test.check/ret {:result true,
                                    :num-tests 1000,
                                    :seed 1531413555637},
      :sym clojure-redpoint.roster/set-gift-pair-in-gift-history})
    

    其参数符合:

    (s/conform (s/or :input-hist (s/and
                                   (s/cat :g-hist :unq/gift-history
                                          :g-year (s/and int? #(> % -1))
                                          :g-pair :unq/gift-pair)
                                   #(<= (:g-year %) (count (:g-hist %))))
                     :input-nil (s/and
                                  (s/cat :g-hist nil?
                                         :g-year (s/and int? #(> % -1))
                                         :g-pair :unq/gift-pair)
                                  #(<= (:g-year %) (count (:g-hist %)))))
               [[{:giver :GeoHar, :givee :JohLen}] 0 {:giver :RinStaXX, :givee :PauMccXX}])
    =>
    [:input-hist
     {:g-hist [{:giver :GeoHar, :givee :JohLen}],
      :g-year 0,
      :g-pair {:giver :RinStaXX, :givee :PauMccXX}}]
    
    (s/conform (s/or :input-hist (s/and
                                   (s/cat :g-hist :unq/gift-history
                                          :g-year (s/and int? #(> % -1))
                                          :g-pair :unq/gift-pair)
                                   #(<= (:g-year %) (count (:g-hist %))))
                     :input-nil (s/and
                                  (s/cat :g-hist nil?
                                         :g-year (s/and int? #(> % -1))
                                         :g-pair :unq/gift-pair)
                                  #(<= (:g-year %) (count (:g-hist %)))))
               [nil 0 {:giver :RinStaXX, :givee :PauMccXX}])
    =>
    [:input-nil
     {:g-hist nil, :g-year 0, :g-pair {:giver :RinStaXX, :givee :PauMccXX}}]
    

    但当这个“正确”功能被第二个功能使用时:

    (defn set-gift-pair-in-roster [plrs-map plr-sym g-year g-pair]
      (let [plr (get-player-in-roster plrs-map plr-sym)
            gh (get-gift-history-in-player plr)
            ngh (set-gift-pair-in-gift-history gh g-year g-pair)
            nplr (set-gift-history-in-player ngh plr)]
        (assoc plrs-map plr-sym nplr)))
    (s/fdef set-gift-pair-in-roster
            :args (s/cat :plrs-map ::plr-map
                         :plr-sym keyword?
                         :g-year (s/and int? #(> % -1))
                         :g-pair :unq/gift-pair)
            :ret ::plr-map)
    

    消费函数成为消费函数的错误源 nil 我以为已经处理好了):

    (stest/check `set-gift-pair-in-roster)
    =>
    ({:spec #object[clojure.spec.alpha$fspec_impl$reify__2451
                    0x3bbc704a
                    "clojure.spec.alpha$fspec_impl$reify__2451@3bbc704a"],
      :clojure.spec.test.check/ret {:result #error{:cause "Call to #'clojure-redpoint.roster/set-gift-pair-in-gift-history did not conform to spec:
                                                           In: [0] val: nil fails spec: :unq/gift-history at: [:args :input-hist :g-hist] predicate: vector?
                                                           val: {:g-hist nil, :g-year 1, :g-pair {:givee :_+, :giver :RJK/Y24}} fails at: [:args :input-nil] predicate: (<= (:g-year %) (count (:g-hist %)))
    

    我已经尝试过在已使用的函数中更改规范的顺序和分组(嵌套),但是在开始测试正在使用的函数之前,它无法通过以前通过的测试。

    有什么问题吗?

    谢谢您!

    编辑:

    如前所述,下面是完整的代码,以便更好地理解:

    ;Here is an example of The Beatles keeping track of the Xmas gifts
    ;they give to each other (:giver and :givee) each year over time:
    
    (ns clojure-redpoint.roster2
      (:require [clojure.spec.alpha :as s]
                [orchestra.spec.test :as st]
                [clojure.test.check.generators :as gen]
                [clojure.spec.test.alpha :as stest]))
    
    (s/def ::givee keyword?)
    (s/def ::giver keyword?)
    (s/def :unq/gift-pair (s/keys :req-un [::givee ::giver]))
    (s/def ::name string?)
    (s/def :unq/gift-history (s/coll-of :unq/gift-pair :kind vector?))
    (s/def :unq/player (s/keys :req-un [::name :unq/gift-history]))
    (s/def ::plr-map (s/map-of keyword? :unq/player))
    
    
    (defn- get-player-in-roster [plrs-map plr-sym]
      (get plrs-map plr-sym))
    (s/fdef get-player-in-roster
            :args (s/cat :plrs-map ::plr-map :plr-sym keyword?)
            :ret (s/or :found :unq/player
                       :not-found nil?))
    
    (defn- get-gift-history-in-player [plr]
      (get plr :gift-history))
    (s/fdef get-gift-history-in-player
            :args (s/or :input-plr (s/cat :plr :unq/player)
                        :input-nil (s/cat :plr nil?))
            :ret (s/or :found :unq/gift-history
                       :not-found nil?))
    
    (defn set-gift-pair-in-gift-history [g-hist g-year g-pair]
      (if (nil? g-hist)
        [{:giver :none, :givee :none}]
        (assoc g-hist g-year g-pair)))
    (s/fdef set-gift-pair-in-gift-history
            :args (s/with-gen
                    (s/or :input-hist (s/and
                                        (s/cat :g-hist :unq/gift-history
                                               :g-year (s/and int? #(> % -1))
                                               :g-pair :unq/gift-pair)
                                        #(<= (:g-year %) (count (:g-hist %))))
                          :input-nil (s/and
                                       (s/cat :g-hist nil?
                                              :g-year (s/and int? #(> % -1))
                                              :g-pair :unq/gift-pair)
                                       #(<= (:g-year %) (count (:g-hist %)))))
                    #(gen/let [hist (s/gen :unq/gift-history)
                               year (gen/large-integer* {:min 0 :max (max 0 (dec (count hist)))})
                               pair (s/gen :unq/gift-pair)]
                       [hist year pair]))
            :ret :unq/gift-history)
    
    (defn set-gift-history-in-player [g-hist plr]
      (if (or (nil? g-hist) (nil? plr))
        {:name "none", :gift-history [{:giver :none, :givee :none}]}
        (assoc plr :gift-history g-hist)))
    (s/fdef set-gift-history-in-player
            :args (s/or :input-good (s/cat :g-hist :unq/gift-history
                                           :plr :unq/player)
                        :input-hist-nil (s/cat :g-hist nil?
                                               :plr :unq/player)
                        :input-plr-nil (s/cat :g-hist :unq/gift-history
                                              :plr nil?)
                        :input-both-nil (s/cat :g-hist nil?
                                               :plr nil?))
            :ret :unq/player)
    
    (defn set-gift-pair-in-roster [plrs-map plr-sym g-year g-pair]
      (let [plr (get-player-in-roster plrs-map plr-sym)
            gh (get-gift-history-in-player plr)
            ngh (set-gift-pair-in-gift-history gh g-year g-pair)
            nplr (set-gift-history-in-player ngh plr)]
        (assoc plrs-map plr-sym nplr)))
    (s/fdef set-gift-pair-in-roster
            :args (s/cat :plrs-map ::plr-map
                         :plr-sym keyword?
                         :g-year (s/and int? #(> % -1))
                         :g-pair :unq/gift-pair)
            :ret ::plr-map)
    
    (st/instrument)
    
    (def roster-map
      {:RinSta {:name "Ringo Starr", :gift-history [{:giver :RinSta, :givee :PauMcc}]},
       :JohLen {:name "John Lennon", :gift-history [{:giver :JohLen, :givee :GeoHar}]},
       :GeoHar {:name "George Harrison", :gift-history [{:giver :GeoHar, :givee :JohLen}]},
       :PauMcc {:name "Paul McCartney", :gift-history [{:giver :PauMcc, :givee :RinSta}]}})
    
    (s/conform ::plr-map
               (set-gift-pair-in-roster roster-map :PauMcc 0 {:giver :JohLenXXX, :givee :GeoHarXXX}))
    ;=>
    ;{:RinSta {:name "Ringo Starr", :gift-history [{:giver :GeoHar, :givee :JohLen}]},
    ; :JohLen {:name "John Lennon", :gift-history [{:giver :RinSta, :givee :PauMcc}]},
    ; :GeoHar {:name "George Harrison",
    ;          :gift-history [{:giver :PauMcc, :givee :RinSta}]},
    ; :PauMcc {:name "Paul McCartney",
    ;          :gift-history [{:giver :JohLenXXX, :givee :GeoHarXXX}]}}
    
    ;(stest/check `set-gift-pair-in-roster)
    

    不幸的是,它没有帮助我找到我的错误…

    1 回复  |  直到 6 年前
        1
  •  2
  •   Taylor Wood    6 年前

    问题是您的一个检测函数 set-gift-pair-in-gift-history 当您调用 (stest/check `set-gift-pair-in-roster) 以下内容:

    CompilerException clojure.lang.ExceptionInfo: Call to #'playground.so/set-gift-pair-in-gift-history did not conform to spec:
    In: [0] val: nil fails spec: :unq/gift-history at: [:args :input-hist :g-hist] predicate: vector?
    val: {:g-hist nil, :g-year 1, :g-pair {:givee :A, :giver :A}} fails at: [:args :input-nil] predicate: (<= (:g-year %) (count (:g-hist %)))
    

    这个 check 输出给我们一个最小的输入来重现错误:

    (set-gift-pair-in-roster {} :A 1 {:givee :A, :giver :A})
    

    我们可以看到失败函数的第一个参数是nil。看着 在礼品历史中设置礼品对 的功能规范,有一个可疑的规范涵盖了这个案例:

                :input-nil (s/and
                             (s/cat :g-hist nil?
                                    :g-year (s/and int? #(> % -1))
                                    :g-pair :unq/gift-pair)
                             #(<= (:g-year %) (count (:g-hist %)))))
    

    只有当 g-hist 是零和 g-year 0 ,但是 :g-year 会产生很多数字 0个 是的。这就是为什么当 instrument 预计起飞时间。

    工具 检查 显示了程序之间的差异 明确规定 行为和它的实际行为。我会先想想 在礼品历史中设置礼品对 当它的第一个参数为零时应该指定。当第一个参数为nil时,实现不关心其他参数,因此可以调整函数规范以反映:

    :input-nil (s/cat :g-hist nil? :g-year any? :g-pair any?)
    

    有了这些变化,您的顶级功能应该 检查 成功。