【问题标题】:How do you spec a sequence/collection of values as one value to a function in clojure?您如何将值的序列/集合指定为 clojure 中函数的一个值?
【发布时间】:2016-08-22 09:33:12
【问题描述】:

我正在尝试以这样一种方式指定一个函数,即它将两个字符串的序列作为函数的第一个参数。

这是我尝试过的:

(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? 类型。我想要的是获得一个参数,由作为一个集合传递的两个字符串组成。

【问题讨论】:

    标签: clojure clojure.spec


    【解决方案1】:

    来自spec 指南的Sequences 部分:

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

    所以你可以像这样指定print-two-strings

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

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

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

    或者,等效地:

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

    这是我的 Emacs 配置中的 example

    【讨论】:

    • 感谢您对缩进的提示!
    • 此示例基于问题中定义的函数是错误的,因为第二个参数不是可选的。
    • 嗨,Alex,Sam 明白我的问题的意思。答案解决了我实际遇到的问题或挫败感。但是,如果您能提出建议,我很乐意修改和/或澄清问题。
    【解决方案2】:
    ;; 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"])
    

    【讨论】:

    • 你误读了这个问题。 rst 参数仅用于显示原始fdef 无法正常工作。您的 fdef 对于采用“一个参数,由作为一个集合传递的两个字符串组成”的函数是不正确的。
    • 嗯...我明白你在说什么。但我仍然认为你给出的例子是错误的 b/c 它没有规范。
    • 我想 OP 需要澄清他对 rst 参数的意图。但是,由于他的实际问题实际上只是关于 ss 参数,我在回答中正确解决了该参数,我认为您仅仅因为它没有解决问题的切线/附带部分而对我的回答投反对票是不公平的.
    • 我很乐意撤消反对票,但不能 b/c 它已被锁定。我仍然认为 ss 部分至少部分不正确,因为您需要更改 ::two-strings 以获得所需的特定符合 OP(字符串集合,而不是字符串部分的映射)。
    • 实际上,OP 并没有具体说明他想要什么 conform 行为;他只展示了他原来的fdef 的行为方式。不过,我当然可以看到您的 fdefconform 行为可能比我的行为更可取。
    猜你喜欢
    • 1970-01-01
    • 2018-06-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-03
    相关资源
    最近更新 更多