您如何将 sequence/collection 个值指定为 clojure 中函数的一个值?
How do you spec a sequence/collection of values as one value to a function in clojure?
我正在尝试以这样一种方式来指定一个函数,即它将两个字符串的序列作为函数的第一个参数。
这是我试过的:
(ns yoostan-lib.test
(:require [clojure.spec :as s]
[clojure.spec.gen :as gen]))
(s/def ::two-strings (s/cat :s1 string?
:s2 string?))
;; (gen/sample (s/gen ::two-strings) 3)
;; (("" "") ("7" "J") ("Tx1" "oQ"))
(s/fdef print-two-strings
:args (s/cat :ss ::two-strings)
:ret string?)
(defn print-two-strings
[ss & rst]
(with-out-str (clojure.pprint/pprint {:ss ss
:rst rst})))
;; this is what I want
;; (print-two-strings '("oeu" "oeu"))
;; => "{:ss (\"oeu\" \"oeu\"), :rst nil}\n"
;; this is what I get instead
;; (s/exercise-fn `print-two-strings)
;; ([("" "") "{:ss \"\", :rst (\"\")}\n"] [("" "") "{:ss \"\", :rst (\"\")}\n"] [("90" "g") "{:ss \"90\", :rst (\"g\")}\n"] [("IhE" "a6") "{:ss \"IhE\", :rst (\"a6\")}\n"] [("8P5" "70A") "{:ss \"8P5\", :rst (\"70A\")}\n"] [("738a" "41j4") "{:ss \"738a\", :rst (\"41j4\")}\n"] [("M8" "4GD1") "{:ss \"M8\", :rst (\"4GD1\")}\n"] [("" "G") "{:ss \"\", :rst (\"G\")}\n"] [("R" "8s43p") "{:ss \"R\", :rst (\"8s43p\")}\n"] [("C1e" "EY2AUE") "{:ss \"C1e\", :rst (\"EY2AUE\")}\n"])
说清楚。我遇到的问题是 exercise-fn
解释了我给它的 fdef
规范,意思是它可以向我的函数传递两个参数,都是 string?
类型。相反,我想要的是获得一个参数,由作为一个集合传递的两个字符串组成。
来自 spec
指南的 Sequences 部分:
When regex ops are combined, they describe a single sequence. If you need to spec a nested sequential collection, you must use an explicit call to spec
to start a new nested regex context.
因此您可以这样指定 print-two-strings
:
(s/fdef print-two-strings
:args (s/cat :ss (s/spec ::two-strings))
:ret string?)
旁注:我看到您将论点垂直对齐 fdef
,而不是像 spec
指南那样使用两个 space 缩进。如果您使用 CIDER, you can configure it to instead use two-space indentation for that macro, as documented here:
(put-clojure-indent 'clojure.spec/fdef 1)
或者,等价地:
(define-clojure-indent
(clojure.spec/fdef 1))
这是来自我的 Emacs 配置的 example。
;; any of these will work, I'd probably use tuple here
(s/def ::two-strings (s/tuple string? string?))
(s/def ::two-strings (s/coll-of string? :count 2))
(s/def ::two-strings (s/coll-of string? :count 2 :into ())) ;; for lists in conformed value
(s/fdef print-two-strings
:args (s/cat :ss ::two-strings :rst (s/? string?))
:ret string?)
(pprint (s/exercise-fn `print-two-strings))
;;=> ([(["" ""] "") "{:ss [\"\" \"\"], :rst (\"\")}\n"]
[(["H" "4"]) "{:ss [\"H\" \"4\"], :rst nil}\n"]
[(["yZ" "7"] "OU") "{:ss [\"yZ\" \"7\"], :rst (\"OU\")}\n"]
[(["" "FFt"]) "{:ss [\"\" \"FFt\"], :rst nil}\n"]
[(["9" "Q0"]) "{:ss [\"9\" \"Q0\"], :rst nil}\n"]
[(["o" "OuSA"]) "{:ss [\"o\" \"OuSA\"], :rst nil}\n"]
[(["1JN" "bT"]) "{:ss [\"1JN\" \"bT\"], :rst nil}\n"]
[(["IUY" ""]) "{:ss [\"IUY\" \"\"], :rst nil}\n"]
[(["8G" "71H3r3d"]) "{:ss [\"8G\" \"71H3r3d\"], :rst nil}\n"]
[(["qL" "zK3ZXA"] "9PV5X1")
"{:ss [\"qL\" \"zK3ZXA\"], :rst (\"9PV5X1\")}\n"])
我正在尝试以这样一种方式来指定一个函数,即它将两个字符串的序列作为函数的第一个参数。
这是我试过的:
(ns yoostan-lib.test
(:require [clojure.spec :as s]
[clojure.spec.gen :as gen]))
(s/def ::two-strings (s/cat :s1 string?
:s2 string?))
;; (gen/sample (s/gen ::two-strings) 3)
;; (("" "") ("7" "J") ("Tx1" "oQ"))
(s/fdef print-two-strings
:args (s/cat :ss ::two-strings)
:ret string?)
(defn print-two-strings
[ss & rst]
(with-out-str (clojure.pprint/pprint {:ss ss
:rst rst})))
;; this is what I want
;; (print-two-strings '("oeu" "oeu"))
;; => "{:ss (\"oeu\" \"oeu\"), :rst nil}\n"
;; this is what I get instead
;; (s/exercise-fn `print-two-strings)
;; ([("" "") "{:ss \"\", :rst (\"\")}\n"] [("" "") "{:ss \"\", :rst (\"\")}\n"] [("90" "g") "{:ss \"90\", :rst (\"g\")}\n"] [("IhE" "a6") "{:ss \"IhE\", :rst (\"a6\")}\n"] [("8P5" "70A") "{:ss \"8P5\", :rst (\"70A\")}\n"] [("738a" "41j4") "{:ss \"738a\", :rst (\"41j4\")}\n"] [("M8" "4GD1") "{:ss \"M8\", :rst (\"4GD1\")}\n"] [("" "G") "{:ss \"\", :rst (\"G\")}\n"] [("R" "8s43p") "{:ss \"R\", :rst (\"8s43p\")}\n"] [("C1e" "EY2AUE") "{:ss \"C1e\", :rst (\"EY2AUE\")}\n"])
说清楚。我遇到的问题是 exercise-fn
解释了我给它的 fdef
规范,意思是它可以向我的函数传递两个参数,都是 string?
类型。相反,我想要的是获得一个参数,由作为一个集合传递的两个字符串组成。
来自 spec
指南的 Sequences 部分:
When regex ops are combined, they describe a single sequence. If you need to spec a nested sequential collection, you must use an explicit call to
spec
to start a new nested regex context.
因此您可以这样指定 print-two-strings
:
(s/fdef print-two-strings
:args (s/cat :ss (s/spec ::two-strings))
:ret string?)
旁注:我看到您将论点垂直对齐 fdef
,而不是像 spec
指南那样使用两个 space 缩进。如果您使用 CIDER, you can configure it to instead use two-space indentation for that macro, as documented here:
(put-clojure-indent 'clojure.spec/fdef 1)
或者,等价地:
(define-clojure-indent
(clojure.spec/fdef 1))
这是来自我的 Emacs 配置的 example。
;; any of these will work, I'd probably use tuple here
(s/def ::two-strings (s/tuple string? string?))
(s/def ::two-strings (s/coll-of string? :count 2))
(s/def ::two-strings (s/coll-of string? :count 2 :into ())) ;; for lists in conformed value
(s/fdef print-two-strings
:args (s/cat :ss ::two-strings :rst (s/? string?))
:ret string?)
(pprint (s/exercise-fn `print-two-strings))
;;=> ([(["" ""] "") "{:ss [\"\" \"\"], :rst (\"\")}\n"]
[(["H" "4"]) "{:ss [\"H\" \"4\"], :rst nil}\n"]
[(["yZ" "7"] "OU") "{:ss [\"yZ\" \"7\"], :rst (\"OU\")}\n"]
[(["" "FFt"]) "{:ss [\"\" \"FFt\"], :rst nil}\n"]
[(["9" "Q0"]) "{:ss [\"9\" \"Q0\"], :rst nil}\n"]
[(["o" "OuSA"]) "{:ss [\"o\" \"OuSA\"], :rst nil}\n"]
[(["1JN" "bT"]) "{:ss [\"1JN\" \"bT\"], :rst nil}\n"]
[(["IUY" ""]) "{:ss [\"IUY\" \"\"], :rst nil}\n"]
[(["8G" "71H3r3d"]) "{:ss [\"8G\" \"71H3r3d\"], :rst nil}\n"]
[(["qL" "zK3ZXA"] "9PV5X1")
"{:ss [\"qL\" \"zK3ZXA\"], :rst (\"9PV5X1\")}\n"])