take-nth

61 views
Skip to first unread message

alxtoth

unread,
Oct 27, 2009, 4:36:54 PM10/27/09
to Clojure
Hi,

Started tinkering with Clojure, and wonderring why (take-nth 0 (range
10)) returns an infinite sequence .. Is this really the expected
behaviour?

;
;code bellow is public domain
;
(defn take-nth-proposal
"Returns a lazy seq of every nth item in coll . Or nil if n less
than 1."
[n coll]
(if (< n 1)
nil
(lazy-seq
(when-let [s (seq coll)]
(cons (first s) (take-nth n (drop n s)))))))


-Cheers, Alex

Timothy Pratley

unread,
Oct 27, 2009, 5:26:29 PM10/27/09
to Clojure
Oh that is confusing!
nth indexes from 0 where-as take-nth indexes from 1.
The doc string could be considered misleading, as it says that take-
nth makes a sequence of "nth" items, but clearly it does not:
clojure.core/take-nth
([n coll])
Returns a lazy seq of every nth item in coll.

user=> (take-nth 1 [1 2 3 4])
(1 2 3 4)
user=> (nth [1 2 3 4] 1)
2

Josh Daghlian

unread,
Oct 27, 2009, 9:50:20 PM10/27/09
to Clojure
The docs could use clarification, but it looks like take-nth is doing
what's advertised.

Is there ever a case (I can't think of one) where a programmer really
wants to feed this function a non-positive n? That is, should take-nth
crap out if (< n 1)?
--josh

John Harrop

unread,
Oct 28, 2009, 3:04:06 AM10/28/09
to clo...@googlegroups.com
On Tue, Oct 27, 2009 at 9:50 PM, Josh Daghlian <dagh...@gmail.com> wrote:
The docs could use clarification, but it looks like take-nth is doing
what's advertised.

I don't see anything odd about the behavior of take-nth in regards to indexing:

user=> (take 10 (take-nth 1 (range 100)))
(0 1 2 3 4 5 6 7 8 9)
user=> (take 10 (take-nth 2 (range 100)))
(0 2 4 6 8 10 12 14 16 18)
user=> (take 10 (take-nth 3 (range 100)))
(0 3 6 9 12 15 18 21 24 27)

It always starts with the zeroth item and skips ahead however many elements were specified. The second argument is the n in
"every nth item". (You can think of it as the index of the SECOND item to take, so (take-nth 3 foo) takes index 0 of foo, then index 3, and so on.)

Is there ever a case (I can't think of one) where a programmer really
wants to feed this function a non-positive n? That is, should take-nth
crap out if (< n 1)?

Probably.

Right now, it just seems to fail to advance through the sequence (interestingly, not just with n=0)  so it never terminates. Even with an explicitly finite sequence input:

user=> (take 10 (take-nth 0 [1 2 3 4 5]))
(1 1 1 1 1 1 1 1 1 1)

Perhaps it should just return a length-1 sequence of just the first element of the input, for zero, and bomb for negative? Or retain its current behavior for zero and bomb for negative.

I'd recommend it just bombing for both. The correct interpretation of taking every zeroth item until the end IS to return an infinite sequence of just the first element (the one at index 0), but it's also perturbing to have an operation that normally produces a no-longer sequence produce an infinite one from a finite one.

Timothy Pratley

unread,
Oct 28, 2009, 4:20:57 AM10/28/09
to clo...@googlegroups.com
On Oct 28, 6:04 pm, John Harrop <jharrop...@gmail.com> wrote:
> It always starts with the zeroth item and skips ahead however many elements
> were specified. The second argument is the n in
> "every nth item". (You can think of it as the index of the SECOND item to
> take, so (take-nth 3 foo) takes index 0 of foo, then index 3, and so on.)

Yes I agree it makes perfect sense, but I don't think the doc string
really says that. It is probably just be my obtuseness but attached is
a very minor patch which might be more clear. As to what should happen
with an argument of 0 or less - I have no idea!

take-nth.patch

John Harrop

unread,
Oct 28, 2009, 5:53:49 AM10/28/09
to clo...@googlegroups.com
On Wed, Oct 28, 2009 at 4:20 AM, Timothy Pratley <timothy...@gmail.com> wrote:
On Oct 28, 6:04 pm, John Harrop <jharrop...@gmail.com> wrote:
> It always starts with the zeroth item and skips ahead however many elements
> were specified. The second argument is the n in
> "every nth item". (You can think of it as the index of the SECOND item to
> take, so (take-nth 3 foo) takes index 0 of foo, then index 3, and so on.)

Yes I agree it makes perfect sense, but I don't think the doc string
really says that.

They probably thought it didn't need to, because the function name does say that. :) 
Reply all
Reply to author
Forward
0 new messages