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 [email protected]
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
-~----------~----~----~----~------~----~------~--~---