概述
Clojure是一种高级的,动态的函数式编程语言。 Clojure是基于LISP编程语言设计的,并且具有使其在Java和.Net运行时环境上运行的编译器。
Clojure作为一种编程语言,具有以下几种高级属性:
- 它基于LISP编程语言,使其代码语句比传统的编程语言更小。
- 它是一种函数型编程语言。
- 它专注于基本概念的不变性,你不应该对创建的对象进行任何更改。
- 它可以管理程序员的应用程序的状态。
- 它支持并发。
- 它包含现有的编程语言。 例如,Clojure可以利用整个Java生态系统来管理通过JVM运行的代码
在我们讨论Clojure之前,让我们来简单地描述LISP编程语言。 LISP有一个微小的语言核心,几乎没有语法,有一个强大的宏设施。 有了这些功能,您可以弯曲LISP以满足您的设计,而不是其他方式。 LISP已经存在了很长时间可追溯到1958年
Clojure的官方网站是 http://clojure.org/
Clojure的各种函数具体解析网站是 http://clojuredocs.org/
Clojure基础语法的网站是 https://www.braveclojure.com/introduction/
Clojure安装Emacs后的配置网站 https://github.com/purcell/emacs.d
Clojure练习题网站是 http://www.4clojure.com/
Clojure入门学习的书籍《Clojure编程》(作者 :埃默里克 翻译:徐明明 / 杨寿勋 ),这对于初学者来说比较容易理解的
Clojure有一定的基础可以看一看《Clojure编程乐趣 》
下面是个人的一些学习笔记,供大家参考学习,若有不足或者不正确的地方,请大家给我指出,互相学习。
1.定义属性
定义参数时需要引入两个包: [clojure.spec.alpha :as s]和[spec-tools.core :as st]
(ns alk-wxapi.routes.doctor
[clojure.spec.alpha :as s]
[spec-tools.core :as st])
1.1 定义单个参数
;;定义单个参数 ,定义一个name参数,类型为string类型
(s/def ::name string?)
1.2 定义参数并且有校验
;;定义一个phone参数,类型为string,并且有校验不能为空
(s/def ::phone
(st/spec {:spec string?
:description "手机号"
:reason 不能为空}))
1.3 body里面的多个参数,哪些为必填的,哪些为非必填,必须在前面用(s/def)定义每一个参数,再用(s/keys),
使用(s/keys :req-un [] :opt-un [] ),req-un表示必填,opt-un表示非必填,
(s/def ::id
(st/spec
{:spec string?
:description "主键"
:reason "请先选择一条数据"}))
...继续定义其他属性
;; ::id 这种双冒号的属性是当前这个对象中的, :base/patien-id 这种单个冒号的属性是其他对象中的属性(base中的patien-id属性)
(s/def ::body-data
(s/coll-of (s/keys :req-un [::cost-date ::drug-id ::sum ::count :base/patient-id ::drug-flag]
:opt-un [::id])))
2.参数接收方式
有三种:path,query,body
2.1 path接收方式
路径是哪个的参数可以用path
["/detail/:id"
{:get {:summary "获取详情接口"
:parameters {:path (s/keys :req-un [:base/id])}
:handler (fn [{{{:keys [id]} :path} :parameters}]
(ok (db/get-operation-cure {:id id})))}}]
2.2 body接收方式
当接口是post或者put
方式的时候,在:paramters处用body接收参数,参数特别多,在fn处可以直接用body-params表示多个参数
:put {:summary "修改商品信息"
;;用body接收多个参数
:parameters {:body (s/keys :req-un [::product-id ::product-name]
:opt-un [::product-color ::receiver-name ::receiver-tel])}
;;多个参数在fn中,可以直接叫body-params,而不用一个参数一个参数的写,太繁琐
:handler (fn [{:keys [body-params]}]
(conman.core/with-transaction
[*db*]
(db/update-product! body-params))
{:status 200
:body {:code 1
:message "更新成功"
:data body-params}})}
2.3 query接收方式
当接口为get或者delete
方式的时候,用query接收参数
:get {:summary "根据id获取guestbook--设置cookies"
:description "查询某id的guestbook数据"
:parameters {:query {:id int?}}
:handler (fn [{{{:keys [id]} :query} :parameters}]
{:status 200
:body {:data (db/get-guestbook {:id id})}
:cookies {:my-cookie "123123123"}})}
:put {:summary "删除检查报告"
:parameters {:query (s/keys :req-un [:base/id])}
:handler (fn [{{{:keys [id]} :query} :parameters}]
(ok (conman.core/with-transaction
[*db*]
(db/delete-examine-report! {:id id}))))}
3. 返回值
3.1只有一个返回值
:get {:summary "获取订单信息"
:parameters {}
:handler (fn [_]
{:status 200
:body {:code 1
:message "操作成功"
;;一个返回值 直接是调用获取数据的函数就可以
:data (db/get-product!)}})}
3.2多个返回值:
多个返回值的时候用{}大括号包起来
["/detail"
{:get {
:summary "分页获取商品新"
:parameters {:query (s/keys :req-un [:base/page :base/size])}
:handler (fn [{{{:keys [page size]}:query}:parameters}]
{:status 200
:body {:code 1
:message "成功"
;;多个返回值 ,要用{}大括号包起来,一个返回值为total-elements ,一个为content
:data {:total-elements (->> (db/get-products! {:count true})
(map :total-elements)
(first))
:content (->> (db/get-products! {:page page :size size}))}}})}}]
4.函数如何传参
参数直接用{}大括号包起来 ,括号中第一个数:count为key ,第二个数为value
(db/get-products! {:count true})
(db/get-products! {:page page :size size})
5. 箭头宏
->单箭头宏和->>双箭头宏的区别
作为一个规则,当一个函数在一个奇异的主题上工作时,这个主题就是第一个参数( 比如,conj,assoc ) 。 当函数在序列主题上工作时,该主题是最后一个参数( 比如,map,filter ) 。
因此,-> 和 ->> 被记录为线程的第一个和最后一个参数,但也可以认为它们分别应用于单数或者顺序参数。
例如我们可以将一个向量看作一个奇异对象:
正确使用:作为第一个参数
(-> [1 2 3]
(conj 5))
=> [1 2 3 5]
错误使用:
(-> [1 2 3]
(conj 5))
=> [1 2 3 5]
(->> [1 2 3
(conj 4])
Syntax error compiling at (form-init5287796127994100350.clj:1:1).
Unable to resolve symbol: (conj in this context
或者我们可以把它看作一个序列:
正确使用:作为最后一个参数
(->> [1 2 3]
(map inc))
=> (2 3 4)
错误使用
(-> [1 2 3]
(map inc))
Error printing return value (IllegalArgumentException) at clojure.lang.RT/seqFrom (RT.java:553).
Don't know how to create ISeq from: clojure.core$inc
有一些经验规则:
- 按照惯例,对序列进行操作的核心函数期望序列作为它们的最后一个参数。因此,管道含有map,
filter,remove,reduce,into,等通常称为->>宏。 - 另一方面,对数据结构进行操作的核心函数期望它们作为第一个参数工作的价值。这些措施包括 assoc, update, dissoc,
get和它们的 -in变种。使用这些函数转换地图的管道通常需要->宏。 - 通过Java interop调用方法时,Java对象作为第一个参数传入。在这种情况下,->例如,检查字符串是否有用是很有用的:
(-> a-string clojure.string/lower-case (.startsWith "prefix"))
还要注意更专业的互操作宏 … 和 doto。
最后,有些情况既不适用->也不->>适用。管道可以包含具有不同插入点的函数调用。在这些情况下,您需要使用as->更灵活的替代方案。as->期望两个固定的参数和可变数量的表达式。与此同时->,第一个参数是要通过以下形式进行操作的值。第二个参数是绑定的名称。在每个后续表单中,绑定名称可用于先前表达式的结果。这允许值进入任何参数位置,而不仅仅是第一个或最后一个。
具体详情请看官方文档:https://clojure.org/guides/threading_macros
6.数据结构
数据结构有list清单(…),vertor矢量[…],map地图{…},set设置#{}
list清单-------------相当于链表
vetor矢量----------相当于数组,下标长零开始
map地图------------映射,key-value
set设置-------------#{…}定义一个集合(一组唯一值)
7.注释
#_ --------------块注释, 注释某个括号内或者大括号的内容
; ----------------单行注释
{:summary "删除花费记录"
:parameters {:query {:id string?}}
;;使用#_ 注释fn这部分内容
:handler #_(fn [{{{:keys [id]} :query} :parameters}]
(ok "删除成功"
(db/update-patient-cost-deleted! {:id id})))}