尝试 Clojure Spec 的笔记

栏目: 编程语言 · Clojure · 发布时间: 5年前

内容简介:首先添加依赖, 因为我在 ClojureScript 当中用, 所以用了首先是一个很简单觉得例子, 有

工具当中需要检测数据格式, 试着用了一下 Clojure Spec.

如果英文好, 直接看文档就行了, 也不用这篇笔记, 太琐碎了, 也缺失例子...

https://clojure.org/guides/sp...

首先添加依赖, 因为我在 ClojureScript 当中用, 所以用了 cljs.spec 这个代码.

expound 是一个用于美化 Spec 输出的类库, 直接引用进来.

[cljs.spec.alpha :as s]
[expound.alpha :refer [expound]]

首先是一个很简单觉得例子, 有 s/valid? 判断数据是否符合格式.

首先用 s/def 定义好一个校验的规则, 其中 ::example 会按照命名空间展开.

(s/def ::example boolean?)
(println (s/valid? ::example 1)) ; false
(println (s/valid? ::example true)) ; true

基础的校验用的是函数, 也可以是 string? .

s/conform 表示返回输出的值.. 当然这个是正确的情况, 返回了匹配到的字符串,

(s/def ::example string?)
(println (s/conform ::example "DEMO")) ; DEMO

如果不匹配, 返回值就是 invalid,

(println (s/conform number? ""))
:cljs.spec.alpha/invalid

可以通过 s/explain 来打印失败的原因,

(s/def ::example string?)
(println (s/explain ::example 1))
; 1 - failed: string? spec: :app.main/example

可以看到这个原因比较精确, 但是可读性不怎么样, 就可以用 expound 替换了, 可读性会好很多,

(s/def ::example string?)
(println (expound ::example 1))
-- Spec failed --------------------

  1

should satisfy

  string?

-- Relevant specs -------

:app.main/example:
  cljs.core/string?

-------------------------
Detected 1 error

既然校验规则是函数, 也可以写成,

(s/def ::example #(and (> % 6) (< % 20)))
(println (s/valid? ::example 1)) ; false
(println (s/valid? ::example 10)) ; true
(println (s/valid? ::example 20)) ; false

校验规则也可以组合使用, 最简单就是 s/or , 注意参数中奇数位置都用的 keyword,

(s/def ::example (s/or :as-number number? :as-boolean boolean?))

(let [data 0]
  (if (s/valid? ::example data)
    (println (s/conform ::example data))
    (println (expound ::example data)))))

打印的结果是,

[:as-number 0]

s/or 里直接用函数式简写了, 可以专门定义两个规则出来, 然后再使用,

(s/def ::boolean boolean?)

(s/def ::number number?)

(s/def ::example (s/or :as-number ::number :as-boolean ::boolean))

(if (s/valid? ::example 20)
  (println (s/conform ::example 20))
  (println (expound ::example 20)))

返回依然得到数据,

[:as-number 20]

对于数组的结构的数据, 用 s/coll-of 来判别,

(s/def ::number number?)

(s/def ::example (s/coll-of ::number))

(let [data [1]]
  (if (s/valid? ::example data)
    (println (s/conform ::example data))
    (println (expound ::example data))))

得到,

[1]

s/coll-of 还支持比如 :count 这样的校验, 具体可以再看文档,

(s/def ::example (s/coll-of number? :count 2))

(defn task! []
 (let [data [1]]
   (if (s/valid? ::example data)
     (println (s/conform ::example data))
     (println (expound ::example data)))))
-- Spec failed --------------------

  [1]

should satisfy

  (= 2 (count %))

-- Relevant specs -------

:app.main/example:
  (cljs.spec.alpha/coll-of cljs.core/number? :count 2)

-------------------------
Detected 1 error

对于 Map, 用 s/keys 来判断, :req-un 表示必选项, opt-un 是可选项,

(s/def ::age number?)

(s/def ::name string?)

(s/def ::example (s/keys :req-un [::age] :opt-un [::name]))

(let [data {:age 1, :name "a"}]
  (if (s/valid? ::example data)
      (println (s/conform ::example data))
      (println (expound ::example data))))

得到,

{:age 1, :name a}

如果不满足校验规则, 会准确提示出来, 比如可选项的规则不满足,

(s/def ::age number?)

(s/def ::name string?)

(s/def ::example (s/keys :req-un [::age] :opt-un [::name]))

(let [data {:age 1, :name 1}]
  (if (s/valid? ::example data)
    (println (s/conform ::example data))
    (println (expound ::example data))))
-- Spec failed --------------------

  {:age ..., :name 1}
                   ^

should satisfy

  string?

-- Relevant specs -------

:app.main/name:
  cljs.core/string?
:app.main/example:
  (cljs.spec.alpha/keys :req-un [:app.main/age] :opt-un [:app.main/name])

-------------------------
Detected 1 error

上面用到的 -un 的后缀表示 "unqualified", 如果没有后缀, 意味着 keyword 要根据命名空间展开,

(s/def ::age number?)

(s/def ::name string?)

(s/def ::example (s/keys :req [::age] :opt [::name]))

(let [data {:age 1, :name 1}]
  (if (s/valid? ::example data)
    (println (s/conform ::example data))
    (println (expound ::example data))))

于是就不满足了,

-- Spec failed --------------------

  {:age 1, :name 1}

should contain key: :app.main/age

|           key |    spec |
|---------------+---------|
| :app.main/age | number? |

-- Relevant specs -------

:app.main/example:
  (cljs.spec.alpha/keys :req [:app.main/age] :opt [:app.main/name])

-------------------------
Detected 1 error

就需要改写一下 key, 也用 ::x 的语法带上命名空间,

(s/def ::age number?)

(s/def ::name string?)

(s/def ::example (s/keys :req [::age] :opt [::name]))

(let [data {::age 1, ::name "a"}]
  (if (s/valid? ::example data)
    (println (s/conform ::example data))
    (println (expound ::example data))))

得到,

{:app.main/age 1, :app.main/name a}

Spec 也可以对字符串进行校验, 同时也可以解析得到数据,

其中需要用到 (s/conformer seq) 来对字符串进行转化...

这个写法目前我也不够清晰, 参考了一下例子,

https://gist.github.com/thege...
(s/def ::left-paren #{"("})

(s/def ::right-paren #{")"})

(s/def ::space (s/and string? (s/conformer seq) (s/+ #{" "})))

(s/def ::token (s/and string? (s/conformer seq) (s/+ #{"a" "b" "c"})))

(s/def
 ::example
 (s/cat
  :left-paren
  ::left-paren
  :expr
  (s/+ (s/or :token ::token :space ::space))
  :right-paren
  ::right-paren))

(let [data (seq "(a b)")]
  (if (s/valid? ::example data)
    (println (pr-str (s/conform ::example data)))
    (println (s/explain ::example data))))

最终得到,

{:left-paren "(", :expr [[:token ["a"]] [:space [" "]] [:token ["b"]]], :right-paren ")"}

更多

这里没有罗列 Entity Map 校验的写法. 也没有展示 generation 的效果. 丢了很多东西.

具体看文档. https://clojure.org/guides/sp...


以上所述就是小编给大家介绍的《尝试 Clojure Spec 的笔记》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Algorithms on Strings, Trees and Sequences

Algorithms on Strings, Trees and Sequences

Dan Gusfield / Cambridge University Press / 1997-5-28 / USD 99.99

String algorithms are a traditional area of study in computer science. In recent years their importance has grown dramatically with the huge increase of electronically stored text and of molecular seq......一起来看看 《Algorithms on Strings, Trees and Sequences》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

MD5 加密
MD5 加密

MD5 加密工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具