'Why can't you RSeq an RSeq?
user=> (rseq [:a :b])
(:b :a)
user=> (rseq (rseq [:a :b]))
ClassCastException clojure.lang.APersistentVector$RSeq cannot be cast to
clojure.lang.Reversible clojure.core/rseq (core.clj:1532)
Why can't rseq
accept the result of a previous call to rseq
?
I read in the docstring that the argument has to be (actually, "can be") a vector or sorted-map, and the above shows that it can't be an RSeq
, so I already know that. What I want to know is: is there a good reason for this restriction? Is it just an oversight, or does this restriction provide some important benefit?
Also, is there a convenient workaround for this, other than just never calling rseq
? It's hard to know, when you return an RSeq
from one function, whether some other function somewhere else might call rseq
on it.
I'm asking because it's frustrating to see my code throw exceptions for such surprising reasons. If I knew why this made sense, I might be less likely to make this and similar kinds of errors.
Solution 1:[1]
You cannot call rseq
on a seq, since you need an input collection with constant-time random access to fullfill rseq
's constant-time performance characteristics, and seqs only provide efficient access (iteration) from the head down.
Calling rseq
on the result of rseq
cannot be special-cased to return the original collection since the original collection is never a seq. And if calling rseq
on a RSeq would return something (seq coll)
, that won't make it straightforward to support (rseq (drop x (rseq coll)))
. It's probably those kinds of complications that kept the language implementers from supporting "recursive" rseq at all.
If you need a general reversal function, use reverse
- which will be slower. If you can, you probably just want to keep a reference to (seq coll)
and (rseq coll)
if you need both.
Solution 2:[2]
Because rseq
works only for the special reversible sequences. But the result of its application is an ordinary seq. You can always check if you can rseq
a sequence with a reversible?
predicate:
(defn reverse* [s]
(if (reversible? s)
(rseq s)
(reverse s)))
Why this fallback isn't in rseq
(or reverse
) function itself? The reason is that rseq
should guarantee its execution time predictability, I guess.
If you really need to reverse the collection back later, you would better keep it as a vector for example: (rseq (vec (rseq [1 2 3])))
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Nathan Davis |