Hi,
On Aug 5, 8:05 am, John Harrop <[email protected]> wrote:
> I just saw someone post a bunch of Clojure code to comp.lang.lisp one
> function from which was:
>
> (defn partition-with [pred coll]
> (loop [in coll curr [] out []]
> (if (empty? in)
> (conj out curr)
> (let [obj (first in)]
> (if (pred obj)
> (recur (rest in) [] (conj out curr))
> (recur (rest in) (conj curr obj) out))))))
>
> This looks like it might be generally useful (and the CLL poster thought so
> too); it splits a collection at items determined by the predicate, producing
> a vector of vectors of items not matching the predicate.
You might want to rewrite that to return a lazy-seq, which just
consumes just enough to provide the partition parts.
(defn partition-with
[pred coll]
(letfn [(step
[s]
(lazy-seq
(when-let [s (seq s)]
(let [[fst rst] (split-with (complement pred)
s)]
(cons fst (step (rest rst)))))))]
(step coll)))
> The same CLL poster noticed that conj with two maps for arguments seems to
> duplicate the functionality of merge. I've just tested this and it seems to
> be true, even with key duplications:
>
> user=> (merge {:key2 2 :key3 3} {:key1 1 :key4 4 :key3 7} )
> {:key4 4, :key1 1, :key2 2, :key3 7}
> user=> (conj {:key2 2 :key3 3} {:key1 1 :key4 4 :key3 7} )
> {:key4 4, :key1 1, :key2 2, :key3 7}
>
> This makes merge appear redundant.
Looking at the source `merge` just reduces the maps
using `conj`. `conj` on maps again, expect either a
MapEntry or a vector. Otherwise it `seq`s its argument
and `conj`s the elements onto the map. `seq`ing a map
gives a seq of MapEntries, and hence we get the
observed result.
But the nil handling is different:
user=> (conj nil {:a 5})
({:a 5})
user=> (merge nil {:a 5})
{:a 5}
Also there's merge-with.
> Further experimentation shows that there isn't even a type-safety reason to
> prefer merge.
"type-safety" somehow seems funny in Clojure...
Maybe "abstraction-safety"?
> It doesn't throw on vectors. In fact:
>
> user=> (merge [1 2 3] 4)
> [1 2 3 4]
> user=> (merge '(1 2 3) 4)
> (4 1 2 3)
> user=> (merge #{1 2 3} 8)
> #{1 2 3 8}
> user=> (merge #{1 2 3} 3)
> #{1 2 3}
> user=> (merge #{1 2 3} #{4 3})
> #{1 2 3 #{3 4}}
> user=> (conj #{1 2 3} #{4 3})
> #{1 2 3 #{3 4}}
>
> so merge appears to be strictly synonymous with conj. Curiouser and
> curiouser...
The contract (the docstring) says "maps". What happens
when you feed different things to `merge` is unspecified.
Since it uses internally `conj` it's no surprise, that it also
works with vectors and sets. However you can see that
the result are broken. There might be a hypothetical merge,
which would also work on vectors and sets in a meaningful
way:
user=> (hypothetical-merge [1 2 3 4] [5 6])
[5 6 3 4]
user=> (hypothetical-merge #{1 2} #{2 3})
#{1 2 3}
For lists, however, I don't see such an operation.
Upshot: `conj` and `merge` are not synonyms, cf. nil
handling.
Sincerely
Meikel
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to [email protected]
Note that posts from new members are moderated - please be patient with your
first post.
To unsubscribe from this group, send email to
[email protected]
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---