I also think it's unhelpful for codebases to stray further from the
builtin functions than needed, because it makes that code harder to
read as well.  So I will consider each of these more carefully.

My comments below are of course highly influence by my personal
experiences using Clojure.  I'm quite willing to admit that even if
I've never needed a function, it's certainly possible that everyone
else has.

On Mon, Jan 12, 2009 at 6:48 PM, Jason Wolfe <jawo...@berkeley.edu> wrote:
>
> ;;; Sequences.
>
> (defn combinations "Take a seq of seqs and return a lazy seq of
> ordered combinations (pick 1 from each seq)"
>  [seqs]
>  (if (empty? seqs) '(())
>    (forcat [item (first seqs)]
>      (map #(cons item %) (combinations (rest seqs))))))

I don't think I've ever needed this.  When I've needed similar, I've
used 'for', but being a macro it can be harder to compose and requires
knowing the depth of the nesting ahead of time.  I'm not aware of
anything other builtin that provides this kind of nesting behavior, so
I'd tend to say this pays for itself.

Almost identical to clojure.contrib.lazy-seqs/combinations
(require '[clojure.contrib.lazy-seqs :as ls])

user=> (combinations [[1 2] [4 5] [7 8]])
((1 4 7) (1 4 8) (1 5 7) (1 5 8) (2 4 7) (2 4 8) (2 5 7) (2 5 8))
user=> (ls/combinations [1 2] [4 5] [7 8])
([1 4 7] [1 4 8] [1 5 7] [1 5 8] [2 4 7] [2 4 8] [2 5 7] [2 5 8])

> (defn power-set
>  "Returns a lazy seq of possible subvectors of seq s."
>  [s]
>  (loop [s (seq s), sets [[]]]
>    (if s
>        (recur (rest s) (lazy-cat sets (map #(conj % (first s))
> sets)))
>      sets)))

Never needed it, but looks like a good candidate for clojure-contrib
I certainly wouldn't want to create it from scratch if I did need it.

> (defn random-permutation [s]
>  "Return a random permutation of this seq."
>  (let [arr (to-array s) len (alength arr)]
>    (dotimes [i (dec len)]
>      (let [r (+ i (rand-int (- len i))),
>            prev (aget arr i)]
>        (aset arr i (aget arr r))
>        (aset arr r prev)))
>     (seq arr)))
>
> (defn random-element [s]
>  "Return a random element of this seq"
>  (nth s (rand-int (count s))))

I wouldn't argue against those.

> (defn maximal-elements [f s]
>  "Return a seq of elements of s maximizing (f elt)."
>  (when (seq s)
>    (loop [max-elts (first s),
>           max-val (f (first s)),
>           rest-elts (rest s)]
>      (if (empty? rest-elts)
>          max-elts
>        (let [next-val (f (first rest-elts))]
>          (cond (< next-val max-val) (recur max-elts max-val (rest rest-
> elts))
>                (= next-val max-val) (recur (cons (first rest-elts) max-elts) 
> max-
> val (rest rest-elts))
>                (> next-val max-val) (recur [(first rest-elts)] next-val (rest 
> rest-
> elts))))))))

I'm having a hard time imagining when maximal-elements would be
useful.  What have you used it for?  Looks like a good candidate for
application code. :-)

> (import '(java.util HashSet))
> (defn distinct-elts? "Are all of the elements of this sequence
> distinct?  Works on infinite sequences with repititions, making it
> useful for, e.g., detecting cycles in graphs."
>  [s]
>  (let [hs (HashSet.)]
>    (loop [s (seq s)]
>      (cond (empty? s)                    true
>            (.contains hs (first s))        false
>            :else (do (.add hs (first s)) (recur (rest s)))))))

Is there any reason the builtin 'distinct?' couldn't handle these
cases as well?  What does "elts" stand for?

> (defn concat-elts "Lazily concatenate elements of a seq of seqs." [s]
>  (when (seq s) (lazy-cat (first s) (concat-elts (rest s)))))
>
> (defn lazy-mapcat "Like mapcat but is lazy like map" [f s]
>  (concat-elts (map f s)))
>
> (defn map-when "Like map but discards logically false entries"
>  [fn & seqs]
>  (filter identity (apply map fn seqs)))

Sufficient laziness is another conversation entirely. :-)

> (defn iterate-while [f x]
>  (take-while identity (iterate f x)))

As I said, I've written this out myself.  If it was included I would
certainly use it.  And as you pointed out, the words in its name mean
exactly what they mean in the body.  I'd vote for this.

> (defn chunk "Lazily break s into chunks of length n (or less, for the
> final chunk)."
>  [n s]
>  (when (seq s)
>    (lazy-cons (take n s)
>               (chunk n (drop n s)))))

This is so close to 'partition', that I think I'd prefer there be only
one, or at least have related names.

> (defn mevery? "Like every but takes multiple seq args like map."
>  ([f & seqs]
>     (or (some empty? seqs)
>         (and (apply f (map first seqs))
>              (recur f (map rest seqs))))))

Is that the same as this?

(defn my-mevery? [f & seqs]
  (every? identity (apply map f seqs)))

Someone suggested all the functions that currently take a predicate
and one seq ought to take multiple seqs and work like 'map'.  I
suppose this would include: every? any? some etc.  Seems pretty
convenient, and I've not heard arguments against it.  But without
that, I think I'd rather use 'identity' and 'apply map' than have an
m-version of all the seq functions.

> ;;; Maps
>
> (defn map-map "Like map, but expects f to return pairs/map entries
> that are combined to make a map return value."
>  [f & maps] (reduce #(conj %1 %2) {} (apply map f maps)))

As discussed, perhaps (into {} (map f ...)) is good enough.

> (defmacro lazy-get "Like get but lazy about evaluating the default
> value"
>  [m k d]
>  `(if-let [pair# (find ~m ~k)]
>       (second pair#)
>     ~d))

I've never needed this.  How expensive is your default value?

> (defn safe-get "Like get but throws an exception if not found"
>  [m k]
>  (lazy-get m k (throw (IllegalArgumentException. (format "Key %s not
> found" k)))))

Ah, I see now.  Unless you're storing nils in your map, this is the
same as (or (m k) (throw ...))

I think I'd rather this be a feature of the map itself, rather than
depend on each 'get' to be a 'safe-get'.  Perhaps if 'get' finds
nothing and no default value is given, it could check the map's
metadata for a :default-value or :throw-unless-found item.

> (defn merge-agree "Like merge but returns nil if there are
> inconsistent key/value pairs."
>  ([] {})
>  ([map] map)
>  ([m1 m2 & maps]
>     (when (every? (fn [[k v]] (= v (get m1 k v))) m2)
>       (apply merge-agree (merge m1 m2) maps))))

Maybe if it accepted the = function as a arg (like merge-with) it
would be a little more general?

> (defn merge-reduce "Combines maps, reducing sets of values with same
> key. Assumes nil value = not present.  The first map entry must be a
> real map, but the remaining arguments can be seqs of map entries/key-
> value pairs."
>  ([f ] {})
>  ([f m1 & maps]
>     (reduce (fn [m [k v]]
>               (if-let [v2 (get m k)]
>                   (assoc m k (f v2 v))
>                 (assoc m k v)))
>             m1
>             (concat-elts maps))))

As discussed, this is like the builtin merge-with.

> (defn prln "Print all arguments and return the first argument"
>  [& args] (do (println (apply print-str args)) (first args)))

You use this for debugging?

> (defn xor [& args]
>  (odd? (count (filter identity args))))

Never needed it.

> (defmacro forcat
>  "Like for, but concatenates the results."
>  [& args] `(concat-elts (for ~...@args)))

Maybe instead of adding 'forcat' for symmetry with 'mapcat', we should
ask for 'mapcat' to be removed.

--Chouser

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Clojure" group.
To post to this group, send email to clojure@googlegroups.com
To unsubscribe from this group, send email to 
clojure+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/clojure?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to