Clojure: finding out if a collection is seq-able
So there's list?, seq?, vector?, map? and so on to determine what type of collection the argument is.
What's a good way of telling the difference between
- a map (i.e. something that contains key-value pairs)
- a collection (i.e. 开发者_运维知识库something that contains values)
- a non collection value like a string.
Is there a better way than
#(or (seq? %) (list? %) etc)
using seq?
is about as concise and clean as it gets.
clojure.contrib.core defines:
seqable? function Usage: (seqable? x) Returns true if (seq x) will succeed, false otherwise.
http://clojure.github.com/clojure-contrib/core-api.html
it does what you proposed with one big or
statement of
- already a seq
- an instance of clojure.lang.Seqable
- nil
- instance of Iterable
- an array
- a string
- instance of java.util.Map
Let's not forget about sequential?
:
user=> (sequential? [])
true
user=> (sequential? '())
true
user=> (sequential? {:a 1})
false
user=> (sequential? "asdf")
false
The function seq
right now does only this:
(. clojure.lang.RT (seq coll))
In RT.java
in the latest version of Clojure, you'll find:
static public ISeq seq(Object coll){
if(coll instanceof ASeq)
return (ASeq) coll;
else if(coll instanceof LazySeq)
return ((LazySeq) coll).seq();
else
return seqFrom(coll);
}
static ISeq seqFrom(Object coll){
if(coll instanceof Seqable)
return ((Seqable) coll).seq();
else if(coll == null)
return null;
else if(coll instanceof Iterable)
return IteratorSeq.create(((Iterable) coll).iterator());
else if(coll.getClass().isArray())
return ArraySeq.createFromObject(coll);
else if(coll instanceof CharSequence)
return StringSeq.create((CharSequence) coll);
else if(coll instanceof Map)
return seq(((Map) coll).entrySet());
else {
Class c = coll.getClass();
Class sc = c.getSuperclass();
throw new IllegalArgumentException("Don't know how to create ISeq from: " + c.getName());
}
}
An ASeq
or a LazySeq
is already a seq. A Seqable
is something that knows how to return a seq of itself.
That leaves things like Java core classes, which should be seqable but which Clojure can't alter to add a seq
method. Those are currently hard-coded into this list. I wouldn't be surprised if the implementation changed someday, maybe using protocols to extend the Java core classes instead?
All seqables implement clojure.lang.Seqable marker:
(instance? clojure.lang.Seqable x)
Clojure 1.9 provides seqable?
精彩评论