开发者

Which is the most clojuresque way to compare characters and string? (single char string)

开发者 https://www.devze.com 2023-01-20 16:33 出处:网络
I was wondering about which is the best (clojuresque) way to compare a character and a string in Clojure.

I was wondering about which is the best (clojuresque) way to compare a character and a string in Clojure. Obviously something like that returns false:

(= (first "clojure") "c")

bec开发者_高级运维ause first returns a java.lang.Character and "c" is a single character string. Does exists a construct to compare directly char and string without invoking a cast? I haven't found a way different from this:

(= (str (first "clojure")) "c")

but I'm not satisfied. Any ideas? Bye, Alfredo


How about the straight forward String interop?

(= (.charAt "clojure" 0) \c)

or

(.startsWith "clojure" "c")

It should be as fast as it can get and doesn't allocate a seq object (and in your second example an additional string) which is immediately thrown away again just to do a comparison.


Character literals are written \a \b \c ... in Clojure so you can simply write

(= (first "clojure") \c)


strings can be directly indexed without building a sequence from then and taking the first of that sequence.

(= (nth "clojure" 0) \c) 
=> true

nth calls through to this java code:

static public Object nth(Object coll, int n){
    if(coll instanceof Indexed)
        return ((Indexed) coll).nth(n);    <-------
    return nthFrom(Util.ret1(coll, coll = null), n);
}

which efficiently reads the character directly.

first call through to this java code:

static public Object first(Object x){
    if(x instanceof ISeq)
        return ((ISeq) x).first();
    ISeq seq = seq(x);    <----- (1)
    if(seq == null)
        return null;
    return seq.first();   <------ (2)
}

which builds a seq for the string (1) (building a seq is really fast) and then takes the first item from that seq (2). after the return the seq is garbage.

Seqs are clearly the most idomatic way of accessing anything sequential in clojure and I'm not knocking them at all. It is interesting to be aware of what you are creating when. switching out all your calls to first with calls to nth is likely to be a case of premature optimization. if you want the 100th char in the string i would suggest using an indexed access function like nth

in short: don't sweat the small stuff :)


Fundamentally (at least on the Clojure level — though see Kotarak's answer and others for alternatives to this), you're comparing two sequences: "clojure" and "c". The condition of equality is that the first element of each sequence is equal. So if you want to express this directly you can do

(apply = (map first ["clojure" "c"]))

or the other way around, where you create a lazy sequence over the equality comparison between each pair of characters, and just take the first element of it:

(first (map = "clojure" "c"))


You could use the take function from clojure.contrib.string. Or write your own function that returns the first char if that's something you need frequently.


You can just use str, as you did in your second example. There isn't really anything wrong with that. I mean, you could call first on "c" as well to make it a character, but it wont really make a difference. Is there any reason why you don't like this? It's not really adding much to your code by calling str on the character.


user=> (= (subs "clojure" 0 1) "c")
true
user=> (= (str (first "clojure") "c"))
true


These days you don't necessarily have to use Java interop:

(clojure.string/starts-with? "clojure" "c")

starts-with? is just a thin wrapper (around .startsWith).

So now if you use both Clojure and ClojureScript you won't have to remember both the Java and the JavaScript interop.

0

精彩评论

暂无评论...
验证码 换一张
取 消