0

Here's a use of the standard 'contains?' function in Clojure-

(contains? {:state "active", :course_n "law", :course_i "C0"} :state)

and it returns the expected

true

I used the following

Clojure: Idiomatic way to call contains? on a lazy sequence

as a guide for building a lazy-contains? as this is what I need for my present use-case.

The problem I'm facing is that for a map these alternatives are not returning the same answer, giving either a false or a nil response. I've tried looking at the source for contains? and it's slow going trying to understand what's happening so I can correct the lazy-contains? appropriately (for the record Clojure is essentially my first programming language, and my exposure to Java is very limited).

Any thoughts or ideas on how I might approach this? I tried every variant on the linked question I could.

Thanks in advance.

Community
  • 1
  • 1
RatavaWen
  • 147
  • 1
  • 8
  • 1
    What's different about your question that's not covered by Chouser's answer to the question you linked? The answer there sounds like a great answer to your question; if you want some other answer please edit this question to be clearer. – amalloy Jul 21 '14 at 07:28
  • possible duplicate of [Clojure: Idiomatic way to call contains? on a lazy sequence](http://stackoverflow.com/questions/16264813/clojure-idiomatic-way-to-call-contains-on-a-lazy-sequence) – Thumbnail Jul 21 '14 at 08:52
  • @Thumbnail That name is taken, `(:added (meta #'some?)) ;=> "1.6"`, but it is the complement of `nil?`. – A. Webb Jul 21 '14 at 13:27

3 Answers3

2

Edited to remove the error pointed out by @amalloy.


I think your problem is with the way that maps present themselves as sequences.

Given

(def data {:state "active", :course_n "law", :course_i "C0"})

then

(seq data)
;([:state "active"] [:course_i "C0"] [:course_n "law"])

... a sequence of key-value pairs.

So if we define (following @chouser)

(defn lazy-contains? [coll x]
  (some #(= x %) coll))

... then

(lazy-contains? data :state)
;nil

... a false result, whereas ...

(lazy-contains? data [:state "active"])
;true

This is what @Ankur was getting at, showing you a function treating a map as a sequence consistent with contains? on the map itself.

  • The standard contains? works with keyed/indexed collections - maps or sets or vectors - where it tests for the presence of a key.
  • Our lazy-contains? works with anything sequable, including all the standard collections, testing for the presence of a value.

Given the way that keyed/indexed collections present as sequences, these are bound to be inconsistent.

Community
  • 1
  • 1
Thumbnail
  • 13,293
  • 2
  • 29
  • 37
  • I don't think there was ever a time that `contains?` worked on sequences. I've dug through the git logs too, and can't find any evidence of such a time; are you sure what you're saying is true? If so, can you provide a git SHA which includes this behavior? – amalloy Jul 21 '14 at 20:19
  • @amalloy Thank you. I misread the question [Clojure: Idiomatic way to call contains? on a lazy sequence](http://stackoverflow.com/questions/16264813/clojure-idiomatic-way-to-call-contains-on-a-lazy-sequence). – Thumbnail Jul 22 '14 at 08:54
  • @amalloy Sorry to have caused you needless work by carelessly jumping to an erroneous conclusion. I'll be more careful in future. – Thumbnail Jul 22 '14 at 11:53
1

You can try the below implementation (for maps only):

(defn lazy-contains? [col key]
  (some (fn [[k v]] (= k key)) col))

Remember, contains? is to check the existence of a key in a collection, in maps the key is obvious, in other supported collections (like vector) the key is the index.

Ankur
  • 33,367
  • 2
  • 46
  • 72
1

A "lazy" implementation of contains? is undesirable where checking for presence

  • of a key in a hash-map or of a value in a set

    (contains? #{:foo} :foo}) => true
    (contains? {:foo :bar} :foo) => true
    
  • of an index of a vector array or string.

    (contains? [:foo] 0) => true
    (contains? (int-array 7) 6) => true
    (contains? "foo" 2) => true
    

Quoting from the contains? docstring:

'contains?' operates constant or logarithmic time; it will not perform a linear search for a value.

some is a tool for linear searching. When searching for an element in a set or vector, it can take the input sequence length times as long as contains? or longer in the worst case and will take more time than contains? in almost every case.

contains? can't be implemented "lazy" as it does not produce a sequence. However, some stops consuming a lazy sequence as soon as it has determined a return value.

(some zero? (range))
;; true

Notice that maps and sets are never sequential or lazy.

Leon Grapenthin
  • 9,246
  • 24
  • 37