Hi all,

I've got lots of utilities that I think people might find useful, and
might fit well in clojure.contrib (just sent in my contrib agreement
last week). I don't know how the process works to get code in there,
but I figured I'd go ahead and post them all here so people can chime
in about what they like / don't like first.

Some of these depend on each-other, but I think the collection as a
whole is self-sufficient.

;;; 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))))))

(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)))

(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))))

(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))))))))

(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)))))))

(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)))

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

(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)))))

(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))))))

;;; 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)))

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

(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)))))

(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))))

(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))))

;;; Miscellaneous

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

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

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

(defmacro assert-is
  "Like assert, but prints some more info (like is) about the
offending form (may multiple eval on error)"
  ([form] `(assert-is ~form ""))
  ([form format-str & args]
     `(when-not ~form
        (throw (Exception. (str (format ~format-str ~...@args)
                                ": Got " '~form " as " (cons '~(first form) 
(list ~@(rest
form)))))))))



(I have more too, but these are the ones I use most frequently.)
Comments/criticisms welcome.  Also, many of these are placeholders for
more efficient versions, which I planned on doing if needed (but would
be happy to do if they're going into clojure.contrib).

-Jason
--~--~---------~--~----~------------~-------~--~----~
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