contains? for vectors

513 views
Skip to first unread message

Irakli Gozalishvili

unread,
Feb 28, 2013, 5:14:33 PM2/28/13
to clo...@googlegroups.com
Hi,

Function contains? returns true if key is present in the given collection, although arguably on some data structures
one would expect behaviour to be different. In fact it's already different on sets:

(contains? #{:a :b :c} :a) ; => true

Would that be reasonable to change behaviour for other types too like:

(contains? [:a :b :c] :a)   ; => false
(contains? "foo" "o")       ; => false
(contains? '(1 2 3) 1)       ; => false

So they do return `true` instead ?

If that will break too much code, maybe some other function can be added instead ?

Regards
--
Irakli Gozalishvili
Web: http://www.jeditoolkit.com/

AtKaaZ

unread,
Feb 28, 2013, 5:23:06 PM2/28/13
to clo...@googlegroups.com
=> (contains? '(1 2 3) 1)
IllegalArgumentException contains? not supported on type: clojure.lang.PersistentList  clojure.lang.RT.contains (RT.java:724)

=> *clojure-version*
{:major 1, :minor 5, :incremental 0, :qualifier "RC17"}


=> (contains? "foo" "o")
IllegalArgumentException contains? not supported on type: java.lang.String  clojure.lang.RT.contains (RT.java:724)

=> (contains? [:a :b :c] :a)
false

whoops

contains? clojure.core

Argument Lists:
[coll key]

Documentation:
Returns true if key is present in the given collection, otherwise
  returns false.  Note that for numerically indexed collections like
  vectors and Java arrays, this tests if the numeric key is within the
  range of indexes. 'contains?' operates constant or logarithmic
   time;
  it will not perform a linear search for a value.  See also 'some'.

=> (contains? '[:a :b :c] 1)
true
=> (contains? '[:a :b :c] 3)
false
=> (contains? '[:a :b :c] 2)
true

well, I would not have expected that


--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 



--
Please correct me if I'm wrong or incomplete,
even if you think I'll subconsciously hate it.

AtKaaZ

unread,
Feb 28, 2013, 5:43:38 PM2/28/13
to clo...@googlegroups.com
the code for that is this:
clojure.lang.APersistentVector.containsKey(Object)

public boolean containsKey(Object key){
    if(!(Util.isInteger(key)))
        return false;
    int i = ((Number) key).intValue();
    return i >= 0 && i < count();
}

if you ask me, it should throw rather than return false, that is, if anyone cares about not introducing subtle errors easily

AtKaaZ

unread,
Feb 28, 2013, 5:44:12 PM2/28/13
to clo...@googlegroups.com
by errors I mean bugs

Michael Gardner

unread,
Feb 28, 2013, 5:59:13 PM2/28/13
to clo...@googlegroups.com
This is a sore spot that has been discussed many times on this list[1]. The short version is that many people agree that the name "contains?" is misleading to newcomers, but according to Rich it's not changing any time soon[2]. What you want for linear searches is 'some (as mentioned in the doc for 'contains?).

Also, why are you quoting your vectors (e.g. '[:a :b :c] instead of just [:a :b :c])?

[1] Most recently, https://groups.google.com/d/msg/clojure/wNFdG11zMMo/OC9ezSn9nRAJ
[2] https://groups.google.com/d/msg/clojure/bSrSb61u-_8/3-wjAkJ4VJgJ

AtKaaZ

unread,
Feb 28, 2013, 6:17:58 PM2/28/13
to clo...@googlegroups.com
On Thu, Feb 28, 2013 at 11:59 PM, Michael Gardner <gard...@gmail.com> wrote:
This is a sore spot that has been discussed many times on this list[1]. The short version is that many people agree that the name "contains?" is misleading to newcomers, but according to Rich it's not changing any time soon[2]. What you want for linear searches is 'some (as mentioned in the doc for 'contains?).

Also, why are you quoting your vectors (e.g. '[:a :b :c] instead of just [:a :b :c])?
I accidentally quoted those in my repl tries and I ended up recalling the last variant (Ctrl+Up in repl) when I tried the 1 3 2 params.
Thank you for the links Michael.

"
Ambrose Bonnaire-Sergeant
9/3/12
'contains?' tests if a key is in a collection.

Vector is an associative data structure, with keys being indexes.
A vector of length 3 has the key 2, but not key 3.
"

According to this, can a vector have keys that are not numbers? like :a , if not, then wouldn't it make more sense that
(contains? [:a :b :c] :a)  would throw ? It's probably just me.

Thanks.

 
 
--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


Irakli Gozalishvili

unread,
Feb 28, 2013, 6:52:29 PM2/28/13
to clo...@googlegroups.com
Thanks Michael,

This links are helpful. As far as I can see nothing like contains-val? or seq-contains? has being added though :(
I'm trying to use cljs for performance sensitive code and high order functions like `some` don't really do it, while
indexOf on strings in JS are well optimised by engines.

Maybe something else can be considered like `includes?` to do that instead ? Also Rich says no better name was
suggest for includes?, I think `has?` would have being a lot better name. Don't know if it makes sense to reply to that
post at this point.


Regards
--
Irakli Gozalishvili
Web: http://www.jeditoolkit.com/

Tassilo Horn

unread,
Mar 1, 2013, 2:30:25 AM3/1/13
to Irakli Gozalishvili, clo...@googlegroups.com
Irakli Gozalishvili <rfo...@gmail.com> writes:

> Maybe something else can be considered like `includes?` to do that
> instead ? Also Rich says no better name was suggest for includes?, I
> think `has?` would have being a lot better name. Don't know if it
> makes sense to reply to that post at this point.

I think member? is a rather good name:

(defn member?
"Returns true iff `e` is a member of `coll`."
[e ^java.util.Collection coll]
(if (seq coll)
(.contains coll e)
false))

Bye,
Tassilo

Michael Gardner

unread,
Mar 1, 2013, 12:03:20 PM3/1/13
to clo...@googlegroups.com
On Feb 28, 2013, at 17:17 , AtKaaZ <atk...@gmail.com> wrote:

> According to this, can a vector have keys that are not numbers? like :a , if not, then wouldn't it make more sense that
> (contains? [:a :b :c] :a) would throw ? It's probably just me.

This is a reasonable point, and one I haven't seen made before. The only problem I can see with throwing an exception is that one might sometimes wish to deal with associative containers in a generic fashion, and thus end up asking a vector if it contains a keyword or a string without doing so explicitly. In that case you'd not want it to throw an exception any more than the other associative collection types would.

AtKaaZ

unread,
Mar 1, 2013, 2:36:05 PM3/1/13
to clo...@googlegroups.com
I don't think I understand what you mean (could you rephrase/example?), I cannot think of a case when I wouldn't want it to throw when I'm passing a non-number (contains? [:a :b :c] :a), I mean, rather than just silently ignoring.
It;s not unlike this:
=> (contains? '(:a :b :c) :a)

IllegalArgumentException contains? not supported on type: clojure.lang.PersistentList  clojure.lang.RT.contains (RT.java:724)

it throws because, it's similarly better than just ignoring it and propagating the bug somewhere else.

I don't know about others but, I would prefer if the constraints of a function's definition are violated by ie. devs calling it wrongly (maybe not even anticipating that it could be called wrongly due to it being like generic - perhaps in a manner that you've explained and I didn't quite understand) are enforced and exceptions thrown rather than silently ignoring therefore allowing subtle bugs.

In a way, it's already so for String:
=> (contains? "aaa" 1)
true
=> (contains? "aaa" 3)
false
=> (contains? "aaa" "a")

IllegalArgumentException contains? not supported on type: java.lang.String  clojure.lang.RT.contains (RT.java:724)
=> (contains? "aaa" 'a)

IllegalArgumentException contains? not supported on type: java.lang.String  clojure.lang.RT.contains (RT.java:724)

Where it's almost as what I'd want for vector to be, namely: the valid keys numbers, are allowed, but when invalid keys are used ie. symbol or other strings, then exception is thrown; but not so for vectors; Granted that the exception thrown seems to be saying something other than: bad key type for passed collection (or so) but still...

=> (contains? [1 3 4] 2)
true
=> (contains? [1 3 4] '2)
true
=> (contains? [1 3 4] "2")
false

So if you were to use this generically on any collection, you'd maybe expect it to throw for vector as it does for string collection.

Thanks for replying, Michael.


--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


Maik Schünemann

unread,
Mar 1, 2013, 3:02:14 PM3/1/13
to clo...@googlegroups.com
the old contails? debate :)
contains? is only intented for associative collections. This explains (somewhat) the behavior on vectors.
They are seen as assocuative collections from indexes to values. 
I'm not exactly sure about this, but I expect 
(contains? c x)  to be true if you can call (get c x). (and get a truthy result except if the value is defined but nil)
To test if a sequential collection contains the value just call .contains or use the (some #{x} c) idiom.

The name IS misleading but after you read the docs it should be clear so i don't think the behaviour of contains? has to be changed.
Greetings

Michael Gardner

unread,
Mar 1, 2013, 3:05:26 PM3/1/13
to clo...@googlegroups.com
On Mar 1, 2013, at 13:36 , AtKaaZ <atk...@gmail.com> wrote:

> I don't think I understand what you mean (could you rephrase/example?), I cannot think of a case when I wouldn't want it to throw when I'm passing a non-number (contains? [:a :b :c] :a), I mean, rather than just silently ignoring.

I'm talking about a case where you have one or more associative containers of unknown types, and you want to be able to generically ask them whether they contain a particular key. E.g.:

(defn has-foo? [c] ;; c is only known to be Associative
(contains? c :foo))

Insofar as a vector can be viewed as a kind of associative container, one might expect/desire this code to just return false if c happens to be a vector. One could see this as a question about the contract promised by the Associative interface: is is always OK to ask an Associative container whether it contains a given key, or are implementations free to accept only certain kinds of keys? I could go either way on that personally, but I can't find any docs that support one interpretation or the other.

> It;s not unlike this:
> => (contains? '(:a :b :c) :a)
> IllegalArgumentException contains? not supported on type: clojure.lang.PersistentList clojure.lang.RT.contains (RT.java:724)
>
> it throws because, it's similarly better than just ignoring it and propagating the bug somewhere else.

Different case, because lists aren't Associative.

> In a way, it's already so for String:
> => (contains? "aaa" 1)
> true
> => (contains? "aaa" 3)
> false
> => (contains? "aaa" "a")
> IllegalArgumentException contains? not supported on type: java.lang.String clojure.lang.RT.contains (RT.java:724)
> => (contains? "aaa" 'a)
> IllegalArgumentException contains? not supported on type: java.lang.String clojure.lang.RT.contains (RT.java:724)

This one's actually quite surprising to me. I don't know why 'contains? throws when given a String and a non-numeric argument, since that's inconsistent with its behavior on vectors (and the exception's description seems inaccurate, since 'contains? *is* supported on Strings). Even more strangely:

user=> (contains? "abc" 1.5)
true

I have no explanation for this.

AtKaaZ

unread,
Mar 1, 2013, 3:06:31 PM3/1/13
to clo...@googlegroups.com
Right, but I'm only after the when-invalid-key is passed:
valid:
=> (contains? "abc" 1)
true
=> (contains? ["a" "b" "c"] 1)
true

invalid:
=> (contains? "abc" "a")

IllegalArgumentException contains? not supported on type: java.lang.String  clojure.lang.RT.contains (RT.java:724)
=> (contains? ["a" "b" "c"] "a")
false

AtKaaZ

unread,
Mar 1, 2013, 3:12:00 PM3/1/13
to clo...@googlegroups.com
user=> (contains? "abc" 1.5)
true

looks like for that, it's coerced to int

else if(key instanceof Number && (coll instanceof String || coll.getClass().isArray())) {
        int n = ((Number) key).intValue();
        return n >= 0 && n < count(coll);
    }
=> (int 1.4)
1
=> (int 1.6)
1
=> (.intValue 1.4)
1
=> (.intValue 1.6)
1



--
--
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clo...@googlegroups.com
Note that posts from new members are moderated - please be patient with your first post.
To unsubscribe from this group, send email to
clojure+u...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
---
You received this message because you are subscribed to the Google Groups "Clojure" group.
To unsubscribe from this group and stop receiving emails from it, send an email to clojure+u...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.


AtKaaZ

unread,
Mar 1, 2013, 3:13:26 PM3/1/13
to clo...@googlegroups.com
and obviously I would like it to throw here instead :) but hey, that's just me - overly obsessed with fail-fast rather that performance first or tolerance
Reply all
Reply to author
Forward
0 new messages