0

我想我可能对函数中嵌套规范的正确顺序有疑问 - 特别s/with-gens/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)

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

4

1 回答 1

2

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。查看set-gift-pair-in-gift-history的功能规范,有一个可疑的规范涵盖了这种情况:

            :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-histis nil 和g-yearis时符合0,但生成器 for:g-year将生成除 之外的许多数字0instrument这就是为什么在它被编辑时调用它失败的原因。

instrument并且check已经表明程序的行为方式与实际行为方式之间存在差异。我会首先考虑set-gift-pair-in-gift-history当它的第一个参数为零时应该如何指定。当第一个 arg 为 nil 时,实现不关心其他参数,因此您可以调整函数规范以反映这一点:

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

通过这种更改,您的顶级功能应该会check成功。

于 2018-07-14T11:42:22.090 回答