如何将序列/值集合指定为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"])

要清楚。 我fdef的问题是exercise-fn解释了我给它的fdef规范,意思是它可以传递我的函数两个参数,都是string?类型string? 。 我想要的是获得一个参数,由两个作为一个集合传递的字符串组成。

I am trying to spec a function in such a way that it takes a sequence of two strings as the first argument to a function.

This is what I have tried:

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

To be clear. The problem I have is that exercise-fn interprets the fdef spec I gave it, as to mean that it can pass my function two arguments, both of type string?. What I would like instead is to get one argument, consisting of two strings passed as one collection.

最满意答案

从spec指南的序列部分:

当组合正则表达式操作时,它们描述单个序列。 如果需要指定嵌套顺序集合,则必须使用对spec的显式调用来启动新的嵌套正则表达式上下文。

所以你可以像这样print-two-strings :

(s/fdef print-two-strings :args (s/cat :ss (s/spec ::two-strings)) :ret string?)

旁注:我看到你将你的参数垂直对齐到fdef ,而不是像spec指南那样使用两个空格缩进。 如果您正在使用CIDER ,则可以将其配置为使用该空间的两空缩进,如下所述:

(put-clojure-indent 'clojure.spec/fdef 1)
 

或者,等效地:

(define-clojure-indent
  (clojure.spec/fdef 1))
 

这是我的Emacs配置中的一个示例 。

From the Sequences portion of the spec guide:

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.

So you can spec print-two-strings like this:

(s/fdef print-two-strings :args (s/cat :ss (s/spec ::two-strings)) :ret string?)

Side note: I see that you're vertically aligning your arguments to fdef, rather than using two-space indentation as the spec guide does. If you're using CIDER, you can configure it to instead use two-space indentation for that macro, as documented here:

(put-clojure-indent 'clojure.spec/fdef 1)
 

Or, equivalently:

(define-clojure-indent
  (clojure.spec/fdef 1))
 

Here's an example from my Emacs config.

更多推荐