尝试 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 的笔记》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

C++数据结构与程序设计

C++数据结构与程序设计

克鲁斯 / 钱丽萍 / 清华大学出版社 / 2004-1 / 59.00元

《C++数据结构与程序设计》这本精心制作的课本结合面向对象程序设计和C++强有力的特性,构建数据结构的基本思想,设计了程序和有趣的应用。在此过程中,《C++数据结构与程序设计》探讨了作为软件设计基本工具的问题求解和设计原理、数据抽象、递归和算法的比较分析。《C++数据结构与程序设计》使用真实的案例研究、可重用的软件开发和程序设计项目来增强理解。一起来看看 《C++数据结构与程序设计》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

html转js在线工具
html转js在线工具

html转js在线工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具