26

在 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

4 回答 4

35

count您可以通过使用take-lastdrop-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

如果您只对列表应用两个操作,我认为这两种方法都非常易读,但是当您有像take, map, filter,这样的长字符串时dropfirst使用->>宏可以使代码更易于阅读,甚至可能更容易写。

于 2012-08-22T09:24:24.577 回答
25

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

在 Python 中,

  • 序列由非负数索引的有限有序集;和
  • 列表是一个可变序列,您可以在其中添加切片或从中删除切片。

在 Clojure 中,

  • 序列是一个支持first, rest, 和 的接口cons
  • 列表是一个不可变的顺序集合,带有cons(或 rest)添加(或删除)first元素(返回如此修改的列表,无论如何)。

Clojure 中最接近 Python 列表的是vector。正如Adam Sznajder 建议的那样,您可以使用 对其进行切片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]
于 2014-06-07T08:41:41.333 回答
11

有一个功能subvec。不幸的是,它仅适用于向量,因此您必须转换您的序列:

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

于 2012-08-22T09:27:25.523 回答
5

对序列进行切片有点“代码味道”——序列通常是为项目的顺序访问而设计的。

如果你要做很多切片/连接,有更好的数据结构可用,特别是检查 RRB-Tree 向量实现:

这支持非常高效subveccatvec操作。

于 2014-08-14T04:56:57.427 回答