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