Clojure学习03:数据结构(集合)

Clojure提供了几种强大的数据结构(集合)

一、集合种类

1、vector

相当于数组,如: [2  3   5]  ,  ["ad"  "adas"  "adadsads"]

2、list

相当于链表,如: ‘(2  3   5)  ,  ‘("ad"  "adas"  "adadsads")

与vector相比,[]变成了() ,又因为 ()符号是用于函数调用,为了区别,需要在 ( 前面加上  一个单引号‘

3、map

语法格式如: {:a  1  :b  1}

map是1个或多个 key-value对,其中key标识符前面要求有:标识 。map的key本身就是函数,通过它可以查找它所对应的值。

如:

user=> (def data {:a 1 :b 2} )

user=> (:a data)
1
user=> (:a {:a 1 :b 2} )
1
user=> (:a {:a 3 :b 2} )
3

上面第一个语句定义了一个值data,是个map。 第二个语句是获取关键字:a的值,这里:a就是一个函数。
最后两个语句就是直接从map字面量上查询key对应的值。

4、set

语法格式如: #{值1  值2  ....}

user=> #{1 2 3}
#{1 3 2}

说明,相比java的集合,Clojure的集合对象有如下几个特征:

1) immutable  集合的内容在初始化后是不可修改的,后续对它的操作都会产生新的集合

2)heterogeneous  多相(异种的),集合中的元素不要求必须是同一种数据类型,可以是不同类型数据的集合

3)persistent 持久化的,集合的内容是不可修改的,相关的操作会产生新的集合,但并不是复制的方式,而是有点类似配置管理的机制,只是新增变更的部分,老的部分还是采用原来的数据。这样所有的历史数据都能完整的保留下来。

二、集合的三大操作

Clojure提供了大量的内置通用函数可以操作各种集合。注意没有函数可以修改集合,因为Clojure的集合是不可修改的,但有很多函数可以生成一个新的集合。

1、filter函数

filter函数是函数式编程中对集合操作的三大重要操作之一,几乎所有的支持函数式编程的语言都有类似的方法。

其作用是筛选出满足条件的元素组成一个新的集合返回。

filter函数需要两个参数,第一个是过滤函数,用于检查元素是否符合,第二个是集合本身。结果返回一个list。

如下面例子:

例1:

user=> (def stooges ["Moe" "Larry" "Curly" "Shemp"])
#‘user/stooges
user=> (filter #(> (count %) 3) stooges)
("Larry" "Curly" "Shemp")

上面代码中的count函数是计算字符串的长度, #(> (count %) 3) 是个匿名函数,只有长度大于3的字符串才满足条件。

例2:

user=> (def years [1940 1944 1961 1985 1987])
#‘user/years
user=> (filter #(even? %) years)
(1940 1944)

2、map

map函数是函数式编程中对集合操作的三大重要操作之一,几乎所有的支持函数式编程的语言都有类似的方法。

其作用是 对集合中的每一个元素做处理,最后得到一个新的集合(注意集合类型是列表),新集合的元素个数和原集合一样,但内容可以不一样(包括元素的类型)。

所以map函数 的第一个参数是对元素转换的处理函数,后面的参数是待处理的集合(一个或多个)。

下面我们举例来说明:

例1:

(defn fun [item] (* item 2))   //定义了一个函数,返回的值是对输入参数乘以2

(map fun [1 2 3])   //map函数使用了fun函数,最后map函数调用后的返回结果为 (2 4 6)

可以看出,被处理的集合是vector,但处理后返回的集合类型为list

例2:

user=> (map fun #{1 2 3}) (2 6 4)

可以看出,set被处理后返回的集合类型也是列表,而且因为set本身是无序的,返回的list结果序号与set表面上看的也不一致。

例3:

user=> (map + [2 4] [5 6] [1 2]) (8 12)

user=> (map + [2 4 7] [5 6] [1 2 3 4]) (8 12)

上面两个例子传入的第一个参数是函数是 + , 后面是多个集合。最后的结果是按照最小的集合元素算的。

例4:

user=> (map #(* % 2) [1 2 3]) (2 4 6)

上面代码中传给map的是一个匿名函数  #(* % 2) 。在实际的集合map操作中,大量场景下会传入匿名函数。

Clojure中的匿名函数就类似于 python、java8中的lambda表达式。

3、reduce

reduce函数是函数式编程中对集合操作的三大重要操作之一,几乎所有的支持函数式编程的语言都有类似的方法。

其作用是对集合做处理,得到一个计算后的值。 如sum ,count, max, min 都是reduce操作的特例,只不过这些操作是非常常见和通用的 ,会被提为专门的方法。

如:

user=> (reduce #(+ %1 %2) [1 2 3])
6

上面操作是对集合求和。reduce的第一个参数是一个函数,这里是匿名函数,该匿名第一个参数(用1%代替)是每次迭代的返回值,%2是元素。

每次对元素操作,1%都会重新最后作为参数传入,最后一个元素处理完后%1的值会作为reduce的函数值返回。

user=> (reduce #(* %1 %2) [2 4 6])
48

上面操作是对集合中的元素求乘积。

user=> (reduce #(if (> %1 %2) %1 %2) [10 2 54 3 6])
54
user=> (reduce #(if (< %1 %2) %1 %2) [10 2 54 3 6])
2

上面的两个操作分别是取最大值和最小值。

三、集合的其它操作

下面介绍的集合的操作都是对上述三大操作的一些特例。

1、count函数

该函数用于获取集合中的元素个数

user=> (count [19 "yellow" true])
3
user=> (count ‘(19 "yellow" true))
3
user=> (count #{19 "yellow" true})
3
user=> (count {:a 1 :b 2})
2

从上面例子可以看出,count函数对这四种集合都是适合的。

2、reverse

该函数是对集合中的数据进行反转排列,返回一个新的集合。因为map和set本身是无序的数据结构,所以reverse函数也只对vector和list有意义。

user=> (reverse [2 4 7])
(7 4 2)
user=> (reverse ‘(2 4 7))
(7 4 2)

3、map

map函数的作用是 对集合中的每一个元素做处理,最后得到一个新的集合(注意集合类型是列表),新集合的元素个数和原集合一样,但内容可以不一样(包括元素的类型)。

所以map函数 的第一个参数是对元素转换的处理函数,后面的参数是待处理的集合(一个或多个)。

下面我们举例来说明:

例1:

(defn fun [item] (* item 2))   //定义了一个函数,返回的值是对输入参数乘以2

(map fun [1 2 3])   //map函数使用了fun函数,最后map函数调用后的返回结果为 (2 4 6)

可以看出,被处理的集合是vector,但处理后返回的集合类型为list

例2:

user=> (map fun #{1 2 3})
(2 6 4)

可以看出,set被处理后返回的集合类型也是列表,而且因为set本身是无序的,返回的list结果序号与set表面上看的也不一致。

例3:

user=> (map + [2 4] [5 6] [1 2])
(8 12)

user=> (map + [2 4 7] [5 6] [1 2 3 4])
(8 12)

上面两个例子传入的第一个参数是函数是 + , 后面是多个集合。最后的结果是按照最小的集合元素算的。

例4:

user=> (map #(* % 2) [1 2 3])
(2 4 6)

上面代码中传给map的是一个匿名函数  #(* % 2) 。在实际的集合map操作中,大量场景下会传入匿名函数。

Clojure中的匿名函数就类似于 python、java8中的lambda表达式。

4、apply

该函数的作用是给它传入一个函数和集合,该函数对整个集合进行操作后返回的结果就是apply函数的返回结果。

user=> (apply + [2 4 6])
12
user=> (apply * [2 4 6])
48
user=> (apply - [2 4 6])
-8

5、从集合中获取单个元素

user=> (def stooges ["Moe" "Larry" "Curly" "Shemp"])
#‘user/stooges
user=> (first stooges)
"Moe"
user=> (second stooges)
"Larry"
user=> (last stooges)
"Shemp"
user=> (nth stooges 2)
"Curly"

最后一个方法nth的第2个参数表示获取第几个元素(从0开始,这里2代表第3个元素)。

6、从集合中获取多个元素

user=> (def stooges ["Moe" "Larry" "Curly" "Shemp"])
#‘user/stooges

user=> (next stooges)
("Larry" "Curly" "Shemp")

user=> (nthnext stooges 1)
("Larry" "Curly" "Shemp")

user=> (nthnext stooges 2)
("Curly" "Shemp")
user=> (butlast stooges)
("Moe" "Larry" "Curly")

user=> (drop-last 1 stooges)
("Moe" "Larry" "Curly")
user=> (drop-last 2 stooges)
("Moe" "Larry")

7、some

该函数可以用来判断集合中是否包含某个元素,需要一个用来判断的函数作为参数,另一个参数是集合本身。如:

user=>  (def stooges ["Moe" "Larry" "Curly" "Shemp"])
#‘user/stooges
user=> (some #(= % "Moe") stooges)
true
user=> (some #(= % "Mark") stooges)
nil

可以看出,如果存在则返回true,否则返回nil(为何不返回false呢?)。

上面的写法会看起来比较笨拙,可以用如下的方法达到同样目的:

user=> (contains? (set stooges) "Moe")
true
user=> (contains? (set stooges) "Mark")
false

上面操作,利用set方法将vector转换为set集合,然后利用contains?函数进行判断,看上去会更清晰简单些。

另外一个区别是,当元素不存在时返回的不是nil,而是false

时间: 2024-12-14 02:11:20

Clojure学习03:数据结构(集合)的相关文章

计算机算法学习(1) - 不相交集合数据结构

不相交集合 故名思意就是一种含有多个不相交集合的数据结构.典型的应用是确定无向图中连通子图的个数.其基本操作包括: Make-Set(x):建立一个新的集合,集合的成员是x: Union(x,y): 将包含x和y的集合合并为一个集合: Find-Set(x): 返回指向包含x的集合的指针: 下面是一个例子,(a)是一个无向图,(b)是使用不相交集合来找连通子图的个数.做法是初始为各个顶点为一个集合,然后遍历各个边,把边的端点的集合进行合并,当处理完所有的边,能连通的顶点就在一个集合里了,这样就生

Java学习03

Java学习03 1.java面试一些问题 一.什么是变量 变量是指在程序执行期间可变的数据.类中的变量是用来表示累的属性的,在编程过程中,可以对变量的值进行修改.变量通常是可变的,即值是变化的 二.什么是函数 1.实现特定功能的一段代码2.可以多次循环使用. 三.什么是数组 数组是用来存储相同数据类型的数据集合,可使用共同的名称来应用数组中的数据.数组可以存储任何类型的数据,包括原始数据类型和对象. 2.循环阅读的技巧 外层循环做一次,内层循环做一遍 3.1234转变为4321 int rig

JAVA学习笔记 -- 数据结构

一.数据结构的接口 在Java中所有类的鼻祖是Object类,但是所有有关数据结构处理的鼻祖就是Collection和Iterator接口,也就是集合与遍历. 1.Collection接口 Collection c = new Xx(); // c可以称为Collection接口回调对象,虽然它被声明为Collection类型,但是实例化时实现的是接口的实现类Xx.它的方法也是用来操作实现类的对象. <span style="white-space:pre"> </s

重读《学习JavaScript数据结构与算法-第三版》- 第6章 链表(一)

定场诗 伤情最是晚凉天,憔悴厮人不堪言: 邀酒摧肠三杯醉.寻香惊梦五更寒. 钗头凤斜卿有泪,荼蘼花了我无缘: 小楼寂寞新雨月.也难如钩也难圆. 前言 本章为重读<学习JavaScript数据结构与算法>的系列文章,该章节主要讲述数据结构-链表,以及实现链表的过程和原理. 链表 链表,为什么要有这种数据结构呢?当然,事出必有因! 数组-最常用.最方便的数据结构,But,当我们从数组的起点或中间插入或移动项的成本很高,因为我们需要移动数组元素. 链表,是存储有序的元素集合.链表中的元素在内存中并不

设计模式学习03—抽象工厂模式

1.动机与定义 工厂模式中,一个工厂仅仅能提供一个或一类产品,当产品种类较多,形成产品系列(比方我们要创建跨平台的button,菜单,文本框等等一系列GUI控件: 单纯使用工厂模式会产生大量工厂,并且后期维护也不方便,我们能够从产品中找到规律,假设产品等级相对固定,以后仅仅会新增产品族,那么我们就能够把整个产品族放到一个工厂创建,以后新增其它系统产品族也很方便,例如以下图: 这样的模式就是抽象工厂,工厂方法模式针对的是一个产品等级结构,而抽象工厂模式则须要面对多个产品等级结构,一个工厂等级结构能

索引学习 查找 数据结构 梳理

索引是啥? 索引是数据结构,在数据结构有一章叫查找,在国外的一本书上名字就找索引. 准确的说就是: 加快查找的数据结构. 查找的那一章: 1.有序数组的二分查找 2.二叉查找 ,在此处,为了效率防止退化,引入了平衡的调整. 3.在上述的平衡的定义,为左右高度至多差1,要求太严,调整频露高,于是红黑树应运而生,它对平衡的定义要求最长的比最短的最多2倍,降低平衡要求的目的是提高性能. 红黑树的5条性质如下: 1.节点为黑或红. 2.根和叶子为黑. 3.不能出现红红. 4,每个节点到叶子黑色高度相同.

ThinkPhp学习03

原文:ThinkPhp学习03 一.ThinkPHP 3 的输出      (重点) a.通过 echo 等PHP原生的输出方式在页面中输出 b.通过display方法输出   想分配变量可以使用assign方法 public function index(){ $name="潘达"; $this->assign('name',$name); //将$name分配给变量name,从而让模板获得 $this->display(); } 模板获取 在对应的tpl下创建模块文件夹

Guava学习笔记: guava集合之Multiset

Guava学习笔记: guava集合之Multiset Multiset是什么? Multiset看似是一个Set,但是实质上它不是一个Set,它没有继承Set接口,它继承的是Collection<E>接口,你可以向Multiset中添加重复的元素,Multiset会对添加的元素做一个计数. 它本质上是一个Set加一个元素计数器. Multiset使用示例: package cn.outofmemory.guava.collection; import com.google.common.ba

软件测试之loadrunner学习笔记-02集合点

loadrunner学习笔记-02集合点 集合点函数可以帮助我们生成有效可控的并发操作.虽然在Controller中多用户负载的Vuser是一起开始运行脚本的,但是由于计算机的串行处理机制,脚本的运行随着时间的推移,并不能完全达到同步.这个时候需要手工的方式让用户在同一时间点上进行操作来测试系统并发处理的能力,而集合点函数就能实现这个功能. 可通过将集合点插入到 Vuser 脚本来指定会合位置.在 Vuser 执行脚本并遇到集合点时,脚本将暂停执行,Vuser 将等待 Controller 或控