[ACCEPTED]-Idiomatic sequence slice in Clojure-slice

Accepted answer
Score: 35

You can simplify all the ones using count by 10 using take-last or drop-last instead:

(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

And as suggested in the 9 comments below, you can use the ->> macro to 8 "thread" several operation together. For 7 example, the last two lines could also be 6 written like this:

(->> 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

I think the two methods 5 are both very readable if you are only applying 4 two operations to a list, but when you have 3 a long string like take, map, filter, drop, first then using the 2 ->> macro can make the code much easier to 1 read and probably even easier for to write.

Score: 26

Python's notion of a sequence is very different 10 from Clojure's.

In Python,

  • a sequence is a finite ordered set indexed by non-negative numbers; and
  • a list is a mutable sequence which you can add slices to or remove slices from.

In Clojure,

  • a sequence is an interface supporting first, rest, and cons;
  • a list is an immutable sequential collection with cons (or rest) adding (or removing) first elements (returning lists so modified, anyway).

The 9 nearest thing in Clojure to a Python list 8 is a vector. As Adam Sznajder suggests, you can slice it using subvec, though 7 you can't add or remove slices as you can 6 in Python.

subvec is a fast constant-time operation, whereas 5 drop makes you pay for the number of elements 4 bypassed (take makes you pay for the elements 3 you traverse, but these are the ones you 2 are interested in).

Your examples become 1 ...

(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]
Score: 11

There is a function subvec. Unfortunately, it 2 only works with vectors so you would have 1 to transform your sequence:

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

Score: 5

Slicing a sequence is a bit of a "code 7 smell" - a sequence in general is designed 6 for sequential access of items.

If you are 5 going to do a lot of slicing / concatenation, there 4 are much better data structures available, in 3 particular checkout the RRB-Tree vector 2 implementation:

This supports very efficient 1 subvec and catvec operations.

More Related questions