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

查看所有标签

猜你喜欢:

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

需求

需求

[美] 亚德里安•斯莱沃斯基(Adrian J. Slywotzky)、[美]卡尔•韦伯 (Karl Weber) / 魏薇、龙志勇 / 浙江人民出版社 / 2013-6 / 64.9

《财富汇•需求:缔造伟大商业传奇的根本力量》内容简介:需求,是缔造伟大商业传奇的根本力量。《财富汇•需求:缔造伟大商业传奇的根本力量》呈现了人们无法拒绝、竞争对手无法复制的需求创造的六大关键,在人们无奈接受的现状和心中真正期待的理想的这道鸿沟之上,架设起了一道桥梁。 创造需求,需要解开一个谜团,这个谜团是人类学、心理学、科技、设计、经济学、基础设施以及其他众多因素综合而成的奇特组合。《财富汇......一起来看看 《需求》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

SHA 加密
SHA 加密

SHA 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器