Clojure中的惯用序列切片

时间:2012-08-22 09:14:53

标签: clojure sequence slice

在Python中,有一种方便的方法可以获取名为“切片”的列表的一部分:

a = [1,2,3,4,5,6,7,8,9,10] # ≡ a = range(1,10)
a[:3] # get first 3 elements
a[3:] # get all elements except the first 3
a[:-3] # get all elements except the last 3
a[-3:] # get last 3 elements
a[3:7] # get 4 elements starting from 3rd (≡ from 3rd to 7th exclusive)
a[3:-3] # get all elements except the first 3 and the last 3

在Clojure中使用clojure.repl/doc,我找到了所有这些的等价物,但我不确定它们是不是惯用的。

(def a (take 10 (iterate inc 1)))
(take 3 a)
(drop 3 a)
(take (- (count a) 3) a)
(drop (- (count a) 3) a)
(drop 3 (take 7 a))
(drop 3 (take (- (count a) 3) a))

我的问题是:如何在Clojure中切片序列?换句话说,返回序列不同部分的正确方法是什么?

4 个答案:

答案 0 :(得分:31)

您可以使用counttake-last来简化使用drop-last的所有内容:

(def a (take 10 (iterate inc 1)))
(take 3 a) ; get first 3 elements
(drop 3 a) ; get all elements except the first 3
(drop-last 3 a) ; get all elements except the last 3
(take-last 3 a) ; get last 3 elements
(drop 3 (take 7 a)) ; get 4 elements starting from 3
(drop 3 (drop-last 3 a)) ; get all elements except the first and the last 3

正如下面的评论中所建议的那样,您可以使用->>宏将几个操作“线程化”在一起。例如,最后两行也可以这样写:

(->> a (take 7) (drop 3)) ; get 4 elements starting from 3
(->> a (drop-last 3) (drop 3)) ; get all elements except the first and the last 3

我认为如果你只对列表应用两个操作,那么这两个方法都是非常易读的,但是如果你有一个长字符串,如takemapfilter,{ {1}},drop然后使用first宏可以使代码更容易阅读,甚至可能更容易编写。

答案 1 :(得分:20)

Python的序列概念与Clojure非常不同。

在Python中,

  • a sequence是一个由有限有序集合索引的 非负数;和
  • list 是一个可变序列,可以添加切片或删除切片 切片来自。

在Clojure中,

  • sequence是一个支持firstrest和/的界面 cons;
  • a list是一个不可变的序列集合cons(或 rest)添加(或删除)first元素(返回列表等) 无论如何都要修改。

Clojure中最接近Python列表的是vector。作为Adam Sznajder suggests,您可以使用subvec对其进行切片,但不能像在Python中那样添加或删除切片。

subvec是一个快速的常数时间操作,而drop会让您为绕过的元素数量付出代价(take会让您为遍历的元素付费,但这些是你感兴趣的人)。

你的例子变成......

(def a (vec (range 1 (inc 10))))

(subvec a 0 3)
; [1 2 3]

(subvec a 3)
; [4 5 6 7 8 9 10]

(subvec a 0 (- (count a) 3))
; [1 2 3 4 5 6 7]

(subvec a (- (count a) 3))
; [8 9 10]

(subvec a 3 (+ 3 4))
; [4 5 6 7]

(subvec a 3 (- (count a) 3))
; [4 5 6 7]

答案 2 :(得分:10)

有一个函数subvec。不幸的是,它只适用于矢量,因此您必须转换序列:

http://clojuredocs.org/clojure_core/clojure.core/subvec

答案 3 :(得分:6)

切片序列有点像代码气味" - 通常用于顺序访问项目的序列。

如果要进行大量的切片/连接,可以使用更好的数据结构,特别是检查RRB-Tree向量的实现:

这支持非常高效的subveccatvec操作。