10

I know I can destructure a vector "from the front" like this:

(fn [[a b & rest]] (+ a b))

Is there any (short) way to access the last two elements instead?

(fn [[rest & a b]] (+ a b)) ;;Not legal

My current alternative is to

(fn [my-vector] (let [[a b] (take-last 2 my-vector)] (+ a b))) 

and it was trying to figure out if there is way to do that in a more convenient way directly in the function arguments.

RubenLaguna
  • 21,435
  • 13
  • 113
  • 151

2 Answers2

18

You can peel off the last two elements and add them thus:

((fn [v] (let [[b a] (rseq v)] (+ a b))) [1 2 3 4])
; 7
  • rseq supplies a reverse sequence for a vector in quick time.
  • We just destructure its first two elements.
  • We needn't mention the rest of it, which we don't do anything with.
Thumbnail
  • 13,293
  • 2
  • 29
  • 37
  • 2
    I hadn't used `rseq` before, and it fits perfectly for this question. I timed it, and it's very close to my answer performance-wise. – WolfeFan Dec 09 '15 at 01:35
16
user=> (def v (vec (range 0 10000000)))
#'user/v
user=> (time ((fn [my-vector] (let [[a b] (take-last 2 my-vector)] (+ a b))) v))
"Elapsed time: 482.965121 msecs"
19999997
user=> (time ((fn [my-vector] (let [a (peek my-vector) b (peek (pop my-vector))] (+ a b))) v))
"Elapsed time: 0.175539 msecs"
19999997

My advice would be to throw convenience to the wind and use peek and pop to work with the end of a vector. When your input vector is very large, you'll see tremendous performance gains.

(Also, to answer the question in the title: no.)

WolfeFan
  • 1,447
  • 7
  • 9