《Clojure编程》笔记 第3章 集合类与数据结构
背景简述
本人是一个自学一年Java的小菜鸡,理论上跟大多数新手的水平差不多,但我入职的新公司是要求转Clojure语言的。坊间传闻:通常情况下,最好是有一定Java的开发工作经验,再转CLojure可能容易一些。我入职后的实际经历也确实让我感受到了Clojure的自学难度略大于自学Java,遇到的困难主要与中文资料较少有关,具体为:
1 中文的面向新手的较为系统的教程材料较少,目前个人感觉最好用的还是《CLojure编程 Emerick著》这本书,网上应该很好找,如果大家没有电子版的话可以留言,我看到后就立刻分享给大家
2 中文的网上相关问题和讨论较少, 以前学Java的时候基本遇到的问题用百度就能解决,现在大概率要直接用bing或谷歌,或者直接在stackoverflow(虽然是英文的,但貌似是最好用的IT问答网站)上查
我的这个系列笔记主要是基于 0工作经验的后端开发转学Clojure 的场景下完成的,里面有一些个人观点和个人理解的注释,写的时候是为了便于自己理解相关的概念,现在分享出来一方面是希望能帮助像我一样的新手更好地理解,另一方面也是希望有高手能够发现错误并帮忙斧正,谢谢
一些格式的简单约定:
粗体:比较重要的内容
斜体:我个人理解/观点或是补充内容,大家选择性食用
P15:表示书上第15页
第3章 集合类与数据结构
四种基本数据结构:list:'( );vector:[ ];map { }:;set:#{ }
3.1 抽象优于实现
Clojure的立场:100个函数操作1种数据结构比10个函数操作10种数据结构要好,Clojure的“抽象优于实现”足以跟Java的接口相媲美,但某些微妙的地方使得Clojure对抽象的强调更彻底,能产生更大的威力
下面将介绍Clojure集合中实现的几个主要抽象:
3.1.1 Collection
Collection(集合),所有数据结构都实现了Collection抽象
个人理解Collection抽象就是所有数据结构的基础,这一部分了解即可,主要是要懂得具体实现的数据结构的使用
核心函数:
conj:[coll value],添加元素到集合,添加列表、vector时是加到头部,添加向量时是加到尾部,添加map和set时顺序不一定
seq:获取集合的顺序视图
count:获取集合的元素个数
empty:获取跟所提供集合类型相同的空集合
=:判断两个或多个集合是否相等,这个相等时判断引用相等,使用的是java的equals(地址或被被重写的判断逻辑)定义
3.1.2 Sequence
序列,定义了一个获取并且遍历各种集合的一个顺序视图的一种方法
核心函数:
seq:返回传入参数的一个序列
first、rest、next、last:遍历序列的一些方法,rest和next基本一样,只是返回结果为空时,rest返回空序列,next返回nil
lazy-seq:创建一个内容为表达式结果的惰性序列
可以用seq来输出结果的类型被称为可序列的类型:
所有Clojure集合
所有Java集合
所有Java map
所有java.lang.CharSequence,包括String
实现了java.lang.Iterablede的任意类型
数组
nil
实现了clojure.lang.Seqable接口的类型
注意:序列不是列表,序列是惰性的,计算长度要遍历,而列表不需要
创建序列有三种办法:其他集合调用seq、利用cons、利用list*
cons:[value coll],始终把第一个参数加到第二个参数的头上,而不管第二个参数具体的集合类型,和conj不同
惰性序列:通过lazy-seq创建惰性序列,这个宏接受任意返回值是一个可序列的值的表达式
惰性序列使用的注意点:
- 定义惰性序列的代码尽量不要有副作
- 避免头保持,惰性序列一个元素被实例化之后,只要保持了对这个序列的引用,那么这个元素就会一直保持着了,这意味着只要保持引用,那么序列的元素就不能被垃圾回收,会影响性能,示例见P97
3.1.3 Associative
关系型数据结构Associative接口所抽象的就是把key和value关联起来的结构,比如map
核心函数:
assoc:向集合添加一个新的key-value映射,也可以操作vector,不过要指定插入的坐标
assoc-in:嵌套解构赋值
dissoc:移除一个key-value映射
get:找出指定key的value,可以操作set,如果存在则返回值,不存在返回nil
find:和get类似,只不过返回的不是value,而是key-value,这样就能判断一个map中是否真实存在一个键值对了
contains?:判断是否包含这个key,也可以用在vector中,不过key相当于vector的index
3.1.4 Indexed
索引集合,一般来说,代码是不需要依赖下标的,所以vector部分没有考虑下标的问题
核心函数:
nth:只能接受数字作为查询值,根据下标返回值,如果越界会抛出异常,对错误的容忍度较低
3.1.5 Stack
栈,后进先出的集合,列表和vector都可以当作栈来用
核心函数:
conj:入栈
pop:获取栈顶的值,并移除这个值
peek:获取栈顶的值,但是不移除
3.1.6 Set
退化的map,只能存非重复元素
核心函数:
get:获取值,但是可以指定获取失败的返回值,P104
disj:移除元素
3.1.7 Sorted
有序集合,顺序可以通过comparator接口来定义,只有map和set实现了sorted接口
核心方法:
rseq:反序返回一个集合的元素
subseq:返回一个集合的某一区间的元素的序列
rsubseq:反序返回区间序列
sorted-map或sorted-set:创建有序map或set,见P104
compare方法是按照字典排序法进行排序的,返回值为-1,0,1;
使用比较器和谓词来定义排序规则:
Clojure的所有函数都实现java.util.Comparator接口
任何一个两参谓词都可以实现比较器
sorted-map和sorted-set是通过compare来定义默认规则进行排序的map和set,而sorted-map-by和sorted-set-by是接受一个比较器(任意两参谓语也可以)来定义排序规则
3.2 访问集合元素的简洁方式
集合本身也是函数,并不是必须要写get、nth
集合的key通常也是函数,比如实际开发工作中,便捷的map取值就是最常用到的,(:id {:id "123" :name "arvin"})
3.2.1 习惯用法
通常推荐吧关键字或者符号作为查找函数来使用,这样可以避免NPE,但如果一个集合的key不是关键字或符号类型,那么只能使用集合本身或者get、nth作为查找函数了,具体见P111
3.2.2 集合、key以及高阶函数
some非常适合在条件判断中判断集合是否包含某个元素,更通用的是filter,它返回一个惰性序列
举例:
(some #{1 2 3} [2 3 4 5])
;= 3
3.3 数据结构的类型
Clojure提供了一些具体的数据结构可以使用,这些具体的结构都实现了一个或多个抽象,它们的行为和语义都是由那些他们实现的抽象所定义的
个人认为这一部分是重点要掌握的内容,因为在实际的开发工作中用的最多,尤其是map相关的内容
3.3.1 List
大多数时候用来做函数调用,将其作为存储数据的场景用的不多
结构是一个单向链表,所以seq一个列表是会返回它本身,而不是顺序视图
真正存储数据的列表需要加一个' ,否则其实会被当作函数调用来使用,但是只要有了',那么括号里面的所有内容都不会被求值,比如'(1 2 (+1 3))的输出结果还是(1 2 (+1 3)),所以这个时候要用(list 1 2 (+ 1 3))
可以通过list?判断一个集合是不是列表
3.3.2 vector
是一种顺序数据结构,支持高校随机访问和更改语义,类似于Java的ArrayList,实现了associative、indexed、stack抽象
可以通过[] 字面量、vector、vec来创建
元组(tuple)是vector最常见的使用场景,任何时候想把多个值绑定在一起处理,比如从一个函数中返回多个值,那么就可以把多个值放入一个vector,其实是多个返回值放进一个vector中,见p114
3.3.3 set
就是具体的set,没什么特别要注意的
3.3.4 map
有方法keys和vals,分别返回所有的键和所有的值
map也被用来保存总结信息、索引信息或对应关系,比如group-by
关于:keys的使用虽然在let解构里面重点讲了,但是在这里再提醒一下,因为实际开发中用到的确实非常多
举例:
(def chas {:name "arvin" :age 20 :location "SH"}
(let [{:keys [name age]} chas]
(str name " is " age " years old."))
3.4 不可变性和持久性
集合的重要特征是不可变和持久的
3.4.1 持久性和结构共享
对Clojure不可变数据结构的操作是非常高效的,因为Clojure数据结构是持久的,从而保证修改过的集合和原来的集合共享数据存储,而且保证了两个集合一样的高效率
持久性:不是值序列化、存储等含义,而是一种能够使不可变数据结构的所有修改版本的效率一直保持一致的一种技术,这个技术是通过结构共享实现的
这里的持久性跟我们在数据库里面理解的持久性不一样,要注意区分
3.5 元数据
有关数据的数据是元数据,主要表现形式有:
- 类型生命和控制修饰符
- Java注解是类、方法及方法参数等的元数据
Clojure的元数据可以被关联到任何数据结构,而且始终是一个map的形式
^ 字面量:就是把元数据关联到值的方式
通过(meta var)可以查看元数据
with-meta和vary-meta可以更新元数据
元数据是不会因为数据结构的修改而修改的,因此关于一个结构的定义是能够一直被保存下来的
上一篇:《Clojure编程》笔记 第2章 函数式编程
下一篇:《Clojure编程》笔记 第4章 多线程和并发
《Clojure编程》笔记 第3章 集合类与数据结构的更多相关文章
- C#高级编程笔记之第二章:核心C#
变量的初始化和作用域 C#的预定义数据类型 流控制 枚举 名称空间 预处理命令 C#编程的推荐规则和约定 变量的初始化和作用域 初始化 C#有两个方法可以一确保变量在使用前进行了初始化: 变量是字段, ...
- C#高级编程笔记之第一章:.NET体系结构
1.1 C#与.NET的关系 C#不能孤立地使用,必须与.NET Framework一起使用一起考虑. (1)C#的体系结构和方法论反映了.NET基础方法论. (2)多数情况下,C#的特定语言功能取决 ...
- Python核心编程笔记 第三章
3.1 语句和语法 3.1.1 注释( # ) 3.1.2 继续( \ ) 一般使用换行分隔,也就是说一行一个语句.一行过长的语句可以使用反斜杠( \ ) 分 ...
- 《Clojure编程》笔记 第4章 多线程和并发
目录 背景简述 第4章 多线程和并发 4.0 我的问题 4.1 术语 4.1.1 一个必须要先确定的思考基础 4.2 计算在时间和空间内的转换 4.2.1 delay 4.2.2 future 4.2 ...
- 《Clojure编程》笔记 第2章 函数式编程
目录 背景简述 第2章 函数式编程 背景简述 本人是一个自学一年Java的小菜鸡,理论上跟大多数新手的水平差不多,但我入职的新公司是要求转Clojure语言的.坊间传闻:通常情况下,最好是有一定Jav ...
- 《Clojure编程》笔记 第1章 进入Clojure仙境
目录 背景简述 第1章 进入Clojure仙境 1.1 基础概念 1.2 常用的一些符号 背景简述 本人是一个自学一年Java的小菜鸡,理论上跟大多数新手的水平差不多,但我入职的新公司是要求转Cloj ...
- 《Clojure编程》笔记 第16章 Clojure与web
目录 背景简述 第16章 Clojure与web 16.1 术语 16.2 Clojure栈 16.3 基石:Ring 16.3.1 请求与应答 16.3.2 适配函数 16.3.3 处理函数 16. ...
- 《Clojure编程》笔记 第13章 测试
目录 背景简述 第13章 测试 13.1 术语 13.2 clojure.test 13.2.1 定义测试的两种方式 13.2.1.1 用deftest宏把测试定义成单独的函数 13.2.1.2 用w ...
- 《Clojure编程》笔记 第5章 宏
目录 背景简述 第5章 宏 5.0 术语 5.1 宏到底是什么 5.1.1 宏不是什么 5.1.2 有什么是宏能做而函数不能做的 5.1.3 宏vsRuby的eval 5.2 编写你的第一个宏 5.3 ...
随机推荐
- Spring学习(十一)--Spring MVC
1.MVC模式 (1)视图 通过视图展示应用数据 向应用数据提供更新动作 向控制器提交用户动作 运行控制器选择不同视图 (2)模型提供 封装应用数据状态 响应数据状态查询 提供应用功 ...
- mysql-14-transaction
#TCL /* 事务控制语言 事务:一个或一组sql语句组成一个执行单元,每条sql语句相互依赖 这个执行单元要么全部执行.要么全部失败后回滚 案例:转账 事务的acid属性 1.原子性:事务时一个不 ...
- Python练习题 007:兔子生兔子
[Python练习题 007] 有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? ----------------- ...
- 实验 2:Mininet 实验——拓扑的命令脚本生成
一.实验目的 掌握 Mininet 的自定义拓扑生成方法:命令行创建.Python 脚本编写 二.实验任务 通过使用命令行创建.Python 脚本编写生成拓扑,熟悉 Mininet 的基本功能. 三. ...
- 第2天 | 12天搞定Python,运行环境(超详细步骤)
倘若有人告诉你,他在学习Python编程,却没有安装运行环境,那你赶紧叫他滚,并离他远点,因为他在欺骗你的感情. 没有安装运行环境,程序根本无法跑起来,对错不能知根知底,试问是在学编程,还是在跟空气对 ...
- Nginx 配置 http 强制跳转到 https
个人真实配置 架构:Nginx 反向代理 + Nginx 前端(LNMP) 在 Nginx 反向代理的 虚拟机主机配置文件中,作如下配置: upstream ilexa_cn { server 192 ...
- spring cloud 实现基于Nacos权重的负载均衡
package com.water.step.service.user.nacos; import com.alibaba.nacos.api.exception.NacosException; im ...
- ES异常处理-NoNodeAvailableException
1.问题描述 ES client客户端能创建,但是在用客户端操作时报:NoNodeAvailableException[None of the configured nodes are availab ...
- docker启动服务---------------redis
1. docker拉取镜像 docker pull redis 2 建立配置目录和准备配置文件 mkdir -p /usr/local/docker-redis && cd /usr/ ...
- docker的常用操作之三:网络配置
一, docker安装后容器使用哪些网络类型? 在宿主机执行如下命令: [root@localhost liuhongdi]# docker network ls NETWORK ID NAME DR ...