Clojure中的惯用序列切片

Mir*_*lov 24 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
Run Code Online (Sandbox Code Playgroud)

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

(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))
Run Code Online (Sandbox Code Playgroud)

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

Dao*_*Wen 32

您可以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
Run Code Online (Sandbox Code Playgroud)

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

(->> 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
Run Code Online (Sandbox Code Playgroud)

我认为这两种方法都非常可读的,如果你只应用两个操作的列表,但是当你有一个长串像take,map,filter,drop,first然后使用->>宏可以使代码更容易阅读和可能甚至更容易的写.

  • 我还建议使用` - >>`宏来实现可读性.`( - >> a(drop-last 3)(取4))` (3认同)
  • 我不相信你的 - >>版本更具可读性. (2认同)
  • @Alex Baranosky - 就像我说的那样,在你只应用两个函数的情况下,我认为它没有多大帮助.如果您正在应用更多类似于5个函数的东西,我认为使用线程宏可以使控制流更清晰,因为您不必从内到外阅读.这些只是意见,而不是事实. (2认同)

Thu*_*ail 22

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

在Python中,

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

在Clojure,

  • 一个序列是一个接口,支持first,restcons;
  • 一个列表是不可变的有序集合与cons(或 rest)添加(或去除)first元件(返回如此修改列表,反正).

Clojure中最接近Python列表的是向量.正如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]
Run Code Online (Sandbox Code Playgroud)


Ada*_*der 11

有一个功能subvec.不幸的是,它只适用于矢量,所以你必须转换你的序列:

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


mik*_*era 6

切片序列有点"代码味道" - 一般来说序列是为了顺序访问项目而设计的.

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

这支持非常有效subveccatvec操作.