diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/clojure/contrib/duck_streams/duck_streams.clj | 120 | ||||
-rw-r--r-- | src/clojure/contrib/enum/enum.clj | 47 | ||||
-rw-r--r-- | src/clojure/contrib/except/except.clj | 44 | ||||
-rw-r--r-- | src/clojure/contrib/fcase/fcase.clj | 93 | ||||
-rw-r--r-- | src/clojure/contrib/gen_interface/gen_interface.clj | 179 | ||||
-rw-r--r-- | src/clojure/contrib/javalog/javalog.clj | 98 | ||||
-rw-r--r-- | src/clojure/contrib/lazy_seqs/lazy_seqs.clj | 90 | ||||
-rw-r--r-- | src/clojure/contrib/lib/lib.clj | 483 | ||||
-rw-r--r-- | src/clojure/contrib/memoize/memoize.clj | 32 | ||||
-rw-r--r-- | src/clojure/contrib/ns_utils/ns_utils.clj | 88 | ||||
-rw-r--r-- | src/clojure/contrib/pred/pred.clj | 108 | ||||
-rw-r--r-- | src/clojure/contrib/seq_utils/seq_utils.clj | 79 | ||||
-rw-r--r-- | src/clojure/contrib/sql/sql.clj | 126 | ||||
-rw-r--r-- | src/clojure/contrib/str_utils/str_utils.clj | 44 | ||||
-rw-r--r-- | src/clojure/contrib/string/string.clj | 29 | ||||
-rw-r--r-- | src/clojure/contrib/test_is/test_is.clj | 211 | ||||
-rw-r--r-- | src/clojure/contrib/trace/trace.clj | 55 | ||||
-rw-r--r-- | src/clojure/contrib/zip_filter/xml/xml.clj | 177 | ||||
-rw-r--r-- | src/clojure/contrib/zip_filter/zip_filter.clj | 91 |
19 files changed, 2194 insertions, 0 deletions
diff --git a/src/clojure/contrib/duck_streams/duck_streams.clj b/src/clojure/contrib/duck_streams/duck_streams.clj new file mode 100644 index 00000000..855e8db5 --- /dev/null +++ b/src/clojure/contrib/duck_streams/duck_streams.clj @@ -0,0 +1,120 @@ +;;; duck_streams.clj -- duck-typed I/O streams for Clojure + +;; by Stuart Sierra <mail@stuartsierra.com> +;; April 8, 2008 + +;; Copyright (c) 2008 Stuart Sierra. All rights reserved. The use and +;; distribution terms for this software are covered by the Common +;; Public License 1.0 (http://www.opensource.org/licenses/cpl1.0.php) +;; which can be found in the file CPL.TXT at the root of this +;; distribution. By using this software in any fashion, you are +;; agreeing to be bound by the terms of this license. You must not +;; remove this notice, or any other, from this software. + + +;; This file defines "duck-typed" I/O utility functions for Clojure. +;; The 'reader' and 'writer' functions will open and return an +;; instance of java.io.BufferedReader and java.io.PrintWriter, +;; respectively, for a variety of argument types -- filenames as +;; strings, URLs, java.io.File's, etc. These functions are not very +;; efficient, because they have to perform a number of 'instance?' +;; checks, but they are convenient when you just want to open a file +;; and don't want to deal with all the Java I/O classes. +;; +;; This file also defines two convenience functions, 'spit' (opposite +;; of 'slurp') and 'write-lines' (opposite of 'line-seq'). + + +(clojure/in-ns 'clojure.contrib.duck-streams) +(clojure/refer 'clojure) + +(import '(java.io Reader InputStream InputStreamReader FileReader + BufferedReader File PrintWriter OutputStream + OutputStreamWriter BufferedWriter Writer FileWriter) + '(java.net URI URL MalformedURLException)) + +(defmacro bufr + {:private true} + [reader] + `(new java.io.BufferedReader ~reader)) + +(defn reader + "Attempts to coerce its argument into an open + java.io.BufferedReader. Argument may be an instance of Reader, + BufferedReader, InputStream, File, URI, URL, or String. + + If argument is a String, it tries to resolve it first as a URI, then + as a local file name. URIs with a 'file' protocol are converted to + local file names. + + Should be used inside with-open to ensure the Reader is properly + closed." + [x] + (cond + (instance? BufferedReader x) x + (instance? Reader x) (bufr x) + (instance? InputStream x) (bufr (new InputStreamReader x)) + (instance? File x) (bufr (new FileReader #^File x)) + (instance? URL x) (if (= (. #^URL x (getProtocol)) "file") + (bufr (new FileReader (. #^URL x (getPath)))) + (bufr (new InputStreamReader (. #^URL x (openStream))))) + (instance? URI x) (reader (. #^URI x (toURL))) + (instance? String x) (try (let [url (new URL x)] + (reader url)) + (catch MalformedURLException err + (bufr (new FileReader #^String x)))) + :else (throw (new Exception (str "Cannot coerce " (class x) + " into a Reader."))))) + +(defmacro bufw + {:private true} + [writer] + `(new java.io.PrintWriter (new java.io.BufferedWriter ~writer))) + +(defn writer + "Attempts to coerce its argument into an open java.io.PrintWriter + wrapped around a java.io.BufferedWriter. Argument may be an + instance of Writer, PrintWriter, BufferedWriter, OutputStream, File, + URI, URL, or String. + + If argument is a String, it tries to resolve it first as a URI, then + as a local file name. URIs with a 'file' protocol are converted to + local file names. + + Should be used inside with-open to ensure the Writer is properly + closed." + [x] + (cond + (instance? PrintWriter x) x + (instance? BufferedWriter x) (new PrintWriter #^BufferedWriter x) + (instance? Writer x) (bufw x) ; includes FileWriter + (instance? OutputStream x) (bufw (new OutputStreamWriter x)) + (instance? File x) (bufw (new FileWriter #^File x)) + (instance? URL x) (if (= (. #^URL x (getProtocol)) "file") + (bufw (new FileWriter (. #^URL x (getPath)))) + (throw (new Exception (str "Cannot write to non-file URL <" x ">.")))) + (instance? URI x) (writer (. #^URI x (toURL))) + (instance? String x) (try (let [url (new URL x)] + (writer url)) + (catch MalformedURLException err + (bufw (new FileWriter #^String x)))) + :else (throw (new Exception (str "Cannot coerce " (class x) + " into a Writer."))))) + +(defn write-lines + "Opposite of 'line-seq'. Writes lines (a seq) to writer (an open + java.io.PrintWriter), separated by newlines." + [#^PrintWriter writer lines] + (let [line (first lines)] + (when line + (. writer (write (str line))) + (. writer (println)) + (recur writer (rest lines))))) + +(defn spit + "Opposite of 'slurp'. Writes 'contents' to the file named by + 'filename'." + [filename contents] + (with-open w (#^PrintWriter writer filename) + (. w (print contents)))) + diff --git a/src/clojure/contrib/enum/enum.clj b/src/clojure/contrib/enum/enum.clj new file mode 100644 index 00000000..b417028d --- /dev/null +++ b/src/clojure/contrib/enum/enum.clj @@ -0,0 +1,47 @@ +;;; enum.clj -- Java enum classes in Clojure + +;; by Stuart Sierra, http://www.stuartsierra.com/ +;; May 29, 2008 + +;; Copyright (c) 2008 Stuart Sierra. All rights reserved. The use and +;; distribution terms for this software are covered by the Common +;; Public License 1.0 (http://www.opensource.org/licenses/cpl1.0.php) +;; which can be found in the file CPL.TXT at the root of this +;; distribution. By using this software in any fashion, you are +;; agreeing to be bound by the terms of this license. You must not +;; remove this notice, or any other, from this software. + + +;; This file helps define Java Enums, introduced in Java 1.5. Use it +;; when you need to define an enum to pass to a Java method. +;; +;; This file depends on genclass.clj in the Clojure distribution. + + +(clojure/in-ns 'clojure.contrib.enum) +(clojure/refer 'clojure) + +(defmacro defenum + "Generates and loads a subclass of java.lang.Enum, then + defs symbols as enumerated instances of that class. + + Example: (defenum my.package.MyEnum FOO BAR) + ;; FOO and BAR are now instances of MyEnum + + Java equivalent: enum MyEnum { FOO, BAR }; + + Caveats: + 1. The generated class has no values() method. + 2. The generated class returns false for Class.isEnum(). + 3. Enum.valueOf(Class, String) will not work. + 4. Redefining an enum is allowed, but enumeration resets + to zero." + [class & symbols] + ;; Can't load a class twice, so check first: + (try (. Class (forName (str class))) + (catch java.lang.ClassNotFoundException e + (gen-and-load-class (str class) :extends java.lang.Enum))) + (cons 'do + (map (fn [sym val] + `(def ~sym (new ~class ~(str sym) ~val))) + symbols (iterate inc 0)))) diff --git a/src/clojure/contrib/except/except.clj b/src/clojure/contrib/except/except.clj new file mode 100644 index 00000000..a2fc8521 --- /dev/null +++ b/src/clojure/contrib/except/except.clj @@ -0,0 +1,44 @@ +;; Copyright (c) Stephen C. Gilardi. All rights reserved. The use and +;; distribution terms for this software are covered by the Common Public +;; License 1.0 (http://opensource.org/licenses/cpl.php) which can be found +;; in the file CPL.TXT at the root of this distribution. By using this +;; software in any fashion, you are agreeing to be bound by the terms of +;; this license. You must not remove this notice, or any other, from this +;; software. +;; +;; except.clj +;; +;; scgilardi (gmail) +;; Created 07 July 2008 + +(clojure/in-ns 'clojure.contrib.except) +(clojure/refer 'clojure) + +(clojure.contrib.lib/use '(clojure.contrib string)) + +(defn throw-if + "Throws an exception with a message if pred is true. Arguments are: + + pred class? format format-args* + + class is optional and defaults to Exception. If present, it must be a + Class in the tree under Throwable with a constructor that takes a single + String. + + format is a string as documented for java.util.Formatter. + + format-args are zero or more objects that correspond to the format + specifiers in format." + [pred & args] + (if pred + (let [class-present (instance? Class (first args)) + args (if class-present args (cons Exception args)) + [class fmt & fmt-args] args + ctor (.getConstructor (identity class) (into-array [String])) + message (apply format fmt fmt-args) + exception (.newInstance ctor (into-array [message])) + raw-trace (.getStackTrace exception) + boring? #(not= (.getMethodName %) "doInvoke") + trace (into-array (drop 2 (drop-while boring? raw-trace)))] + (.setStackTrace exception trace) + (throw exception)))) diff --git a/src/clojure/contrib/fcase/fcase.clj b/src/clojure/contrib/fcase/fcase.clj new file mode 100644 index 00000000..8e316f56 --- /dev/null +++ b/src/clojure/contrib/fcase/fcase.clj @@ -0,0 +1,93 @@ +;;; fcase.clj -- simple variants of "case" for Clojure + +;; by Stuart Sierra <mail@stuartsierra.com> +;; April 7, 2008 + +;; Copyright (c) 2008 Stuart Sierra. All rights reserved. The use and +;; distribution terms for this software are covered by the Common +;; Public License 1.0 (http://www.opensource.org/licenses/cpl1.0.php) +;; which can be found in the file CPL.TXT at the root of this +;; distribution. By using this software in any fashion, you are +;; agreeing to be bound by the terms of this license. You must not +;; remove this notice, or any other, from this software. + + +;; This file defines a generic "case" macro called "fcase" which takes +;; the equality-testing function as an argument. It also defines a +;; traditional "case" macro that tests using "=" and variants that +;; test for regular expressions and class membership. + + +(clojure/in-ns 'clojure.contrib.fcase) +(clojure/refer 'clojure) + + +(defmacro fcase + "Generic switch/case macro. 'fcase' is short for 'function case'. + + The 'compare-fn' is a fn of two arguments. + + The 'test-expr-clauses' are value-expression pairs without + surrounding parentheses, like in Clojure's 'cond'. + + The 'case-value' is evaluated once and cached. Then, 'compare-fn' + is called once for each clause, with the clause's test value as its + first argument and 'case-value' as its second argument. If + 'compare-fn' returns logical true, the clause's expression is + evaluated and returned. If 'compare-fn' returns false/nil, we go to + the next test value. + + If 'test-expr-clauses' contains an odd number of items, the last + item is the default expression evaluated if no case-value matches. + If there is no default expression and no case-value matches, fcase + returns nil. + + See specific forms of this macro in 'case' and 're-case'. + + The test expressions in 'fcase' are always evaluated linearly, in + order. For a large number of case expressions it may be more + efficient to use a hash lookup." + [compare-fn case-value & + test-expr-clauses] + (let [test-val-sym (gensym "test_val") + test-fn-sym (gensym "test_fn") + cond-loop (fn this [clauses] + (cond + (>= (count clauses) 2) + (list 'if (list test-fn-sym (first clauses) test-val-sym) + (second clauses) + (this (rest (rest clauses)))) + (= (count clauses) 1) (first clauses)))] + (list 'let [test-val-sym case-value, test-fn-sym compare-fn] + (cond-loop test-expr-clauses)))) + +(defmacro case + "Like cond, but test-value is compared against the value of each + test expression with =. If they are equal, executes the \"body\" + expression. Optional last expression is executed if none of the + test expressions match." + [test-value & clauses] + `(fcase = ~test-value ~@clauses)) + +(defmacro re-case + "Like case, but the test expressions are regular expressions, tested + with re-find." + [test-value & clauses] + `(fcase re-find ~test-value ~@clauses)) + +(defmacro instance-case + "Like case, but the test expressions are Java class names, tested with + 'instance?'." + [test-value & clauses] + `(fcase instance? ~test-value ~@clauses)) + +(defn- in-case-test [test-seq case-value] + (some (fn [item] (= item case-value)) + test-seq)) + +(defmacro in-case + "Like case, but test expressions are sequences. The test expression + is true if any item in the sequence is equal (tested with '=') to + the test value." + [test-value & clauses] + `(fcase in-case-test ~test-value ~@clauses)) diff --git a/src/clojure/contrib/gen_interface/gen_interface.clj b/src/clojure/contrib/gen_interface/gen_interface.clj new file mode 100644 index 00000000..2ab9a601 --- /dev/null +++ b/src/clojure/contrib/gen_interface/gen_interface.clj @@ -0,0 +1,179 @@ +; Copyright (c) Chris Houser, July 2008. All rights reserved. +; The use and distribution terms for this software are covered by the +; Common Public License 1.0 (http://opensource.org/licenses/cpl.php) +; which can be found in the file CPL.TXT at the root of this distribution. +; By using this software in any fashion, you are agreeing to be bound by +; the terms of this license. +; You must not remove this notice, or any other, from this software. + +; Functions for generating interface classes, which can then be loaded +; or saved to a .class file. + +(clojure/in-ns 'clojure.contrib.gen-interface) +(clojure/refer 'clojure) + +(import '(clojure.asm ClassWriter Opcodes Type) + '(java.io File FileOutputStream IOException)) + +(defn- asm-type + "Returns an asm Type object for c, which may be a primitive class + (such as Integer/TYPE), any other class (such as Double), or a + fully-qualified class name given as a string or symbol + (such as 'java.lang.String)" + [c] + (if (instance? Class c) + (Type/getType c) + (Type/getObjectType (.replace (str c) "." "/")))) + +(defn- iname + "Returns the internal name of given class or class name. Cannot be + used for primitive types." + [c] (.getInternalName (asm-type c))) + +(defstruct #^{:private true} spec-map :cname :iname :extends :methods) + +(defn- make-spec + "Returns an interface spec object based on the given description. + cname is the fully-qualified classname (string or symbol) of the + interface to be created. + extends is a collection of classes this interface will extend (each + may be a string, symbol, or a class). These are followed by the + method descriptions, each of which is a vector: [methodName, + arg types, return type]" + [cname extends & methods] + (struct spec-map + (str cname) + (iname cname) + (set (map iname extends)) + (set (map (fn [[mname pclasses rclass]] + [(str mname) + (map asm-type pclasses) + (asm-type rclass)]) + methods)))) + +(defn- spec-from-class + "Returns an interface spec object based on the given class." + [c] + (struct spec-map + (.getName c) + (iname c) + (set (map iname (.getInterfaces c))) + (set (map (fn [m] + [(.getName m) + (map asm-type (.getParameterTypes m)) + (asm-type (.getReturnType m))]) + (.getDeclaredMethods c))))) + +(def #^{:private true} object-iname (iname Object)) + +(defn- spec-bytecode + "Uses the given interface spec object (such as created by make-spec) + to generate a Java interface. Returns a byte array containing the + Java bytecode for the interface. You'll almost always want to use + gen-interface instead." + [{:keys [iname extends methods]}] + (let [cv (ClassWriter. ClassWriter/COMPUTE_MAXS)] + (. cv visit Opcodes/V1_5 (+ Opcodes/ACC_PUBLIC Opcodes/ACC_ABSTRACT + Opcodes/ACC_INTERFACE) + iname nil object-iname + (when (seq extends) + (into-array extends))) + (doseq [mname pclasses rclass] methods + (. cv visitMethod (+ Opcodes/ACC_PUBLIC Opcodes/ACC_ABSTRACT) + mname + (Type/getMethodDescriptor rclass (if pclasses + (into-array pclasses) + (make-array Type 0))) + nil nil)) + (. cv visitEnd) + (. cv toByteArray))) + + +(defn- load-interface-bytecode + [{:keys [cname] :as spec} bytecode] + (let [old-class (try (Class/forName cname) (catch Throwable t nil))] + (if old-class + (when-not (= spec (spec-from-class old-class)) + (throw (Exception. (str "A different class named " + cname " already loaded")))) + (.. clojure.lang.RT + ROOT_CLASSLOADER (defineClass cname bytecode))))) + +(defn- save-interface-bytecode + [path {:keys [cname]} bytecode] + (let [file (File. path (str (.replace cname \. File/separatorChar) + ".class"))] + (try + (.createNewFile file) + (catch IOException e + (throw (Exception. (str "Failed to create " file) e)))) + (with-open f (FileOutputStream. file) + (.write f bytecode)))) + +(defn gen-and-load-interface + "Uses the given interface description to generate a Java interface + and immediately load it. make-spec-args is the interface + description as documented in make-spec. This function is not + generally useful since you'll usually want a .class file in order + to write Java code that uses the generated interface -- see + gen-interface instead." + [& make-spec-args] + (let [spec (apply make-spec make-spec-args)] + (load-interface-bytecode spec (spec-bytecode spec)))) + +(defn gen-and-save-interface + "Uses the given interface description to generate a Java interface + and save it to a .class file. make-spec-args is the interface + description as documented in make-spec. The .class file will be + written into a sub-directory of the given base path (note that the + appropriate sub-directories under path must already exist or this + will throw an exception). If you intend to use this interface + immediately (for example to refer to it in a later gen-interface or + gen-class call), you'll want to use gen-interface instead." + [path & make-spec-args] + (let [spec (apply make-spec make-spec-args)] + (save-interface-bytecode path spec (spec-bytecode spec)))) + +(defn gen-interface + "Uses the given interface description to generate a Java interface, + save it to a .class file, and immediately load it so it's ready + for use by subsequent gen-interface or gen-class calls. The .class + file will be written into a sub-directory of the given base path. + make-spec-args is the interface description as documented in + make-spec." + [path & make-spec-args] + (let [spec (apply make-spec make-spec-args) + bytecode (spec-bytecode spec)] + (load-interface-bytecode spec bytecode) + (save-interface-bytecode path spec bytecode))) + +(comment + +(gen-interface "/tmp" 'net.n01se.Foo [Appendable] + ['foo [] Integer] + ['bar [Integer/TYPE String] Double]) + +; re-genning an identical interface doesn't try to load anything +(gen-interface "/tmp" 'net.n01se.Foo [Appendable] + ['foo [] Integer] + ['bar [Integer/TYPE String] Double]) + +; re-genning a different interface throws an exception +;(gen-interface "/tmp" 'net.n01se.Foo [Appendable] +; ['foo [] Integer]) + +; gen-and-save-interface is used directly in this example because I +; want to refer to a class that's not yet defined in this runtime +; (Other). This is possible because I specify the class as a quoted +; symbol, and then don't load it -- but this isn't really recommended. +; Instead, why not make sure Other is defined -- then you can use +; gen-interface. +(gen-and-save-interface "/tmp" 'net.n01se.Bar ['net.n01se.Other Iterable] + ['baz [] net.n01se.Foo]) + +(prn :isInterface (.isInterface (identity net.n01se.Foo))) +(prn :interfaces (seq (.getGenericInterfaces (identity net.n01se.Foo)))) +(doseq m (seq (.getMethods (identity net.n01se.Foo))) + (prn m)) + +) diff --git a/src/clojure/contrib/javalog/javalog.clj b/src/clojure/contrib/javalog/javalog.clj new file mode 100644 index 00000000..2cb12c48 --- /dev/null +++ b/src/clojure/contrib/javalog/javalog.clj @@ -0,0 +1,98 @@ +;;; javalog.clj -- convenient access to java.util.logging in Clojure + +;; by Stuart Sierra <mail@stuartsierra.com> +;; April 8, 2008 + +;; Copyright (c) 2008 Stuart Sierra. All rights reserved. The use and +;; distribution terms for this software are covered by the Common +;; Public License 1.0 (http://www.opensource.org/licenses/cpl1.0.php) +;; which can be found in the file CPL.TXT at the root of this +;; distribution. By using this software in any fashion, you are +;; agreeing to be bound by the terms of this license. You must not +;; remove this notice, or any other, from this software. + + +;; This file defines some convenience functions for using the Java +;; logging framework from Clojure. It is oriented towards simple +;; development and debugging rather than complex production +;; environments. + + + +(clojure/in-ns 'clojure.contrib.javalog) +(clojure/refer 'clojure) + +(import '(java.util.logging Logger Level ConsoleHandler + FileHandler SimpleFormatter)) + +(def + #^{:tag Logger + :doc "The current java.util.logging.Logger. By default, the + global logger, modified by 'with-logger'."} + *logger* + (. Logger + (getLogger + (. Logger GLOBAL_LOGGER_NAME)))) + +(defmacro log-level + "Translates 'level' (a lower-case keyword) into a static field of + java.util.logging.Level, by name. + + Example: (log-level :severe) => java.util.logging.Level.SEVERE + + If 'level' is not a keyword, it is assumed to be a user-defined + instance of java.util.logging.Level and is returned unchanged." + [level] + (if (keyword? level) + `(. java.util.logging.Level + ~(symbol (. (name level) (toUpperCase)))) + level)) + +(defn root-logger + "Returns the root Logger instance." + ([] (root-logger *logger*)) + ([logger] (let [parent (. logger (getParent))] + (if parent + (recur parent) + logger)))) + +(defn set-console-log-level + "Attempts to set the level of the current logger and the root + ConsoleHandler to 'level' (a java.util.logging.Level). Useful for + debugging at the REPL." + [level] + (let [console-handler + (some (fn [h] (if (instance? ConsoleHandler h) h)) + (. (root-logger) (getHandlers)))] + (if console-handler + (do (. *logger* (setLevel level)) + (. console-handler (setLevel level))) + (throw (new Exception "No ConsoleHandler on root logger."))))) + +(defn add-log-file + "Attaches a log file, using SimpleFormatter, with the given level, + to the named logger. 'level' defaults to ALL. Note: multiple + invocations will create multiple log files, with numbers appended to + the names." + ([logger-name filename] + (add-log-file logger-name filename (. Level ALL))) + ([logger-name filename level] + (let [logger (. Logger (getLogger logger-name)) + handler (new FileHandler filename)] + (. handler (setFormatter (new SimpleFormatter))) + (. handler (setLevel level)) + (. logger (addHandler handler))))) + +(defmacro with-logger + "Executes 'body' with *logger* bound to a logger with the given name + and level. 'level' is expanded with 'log-level'." + [logger-name level & body] + `(binding [*logger* (. Logger (getLogger ~logger-name))] + (. *logger* (setLevel (log-level ~level))) + ~@body)) + +(defmacro log + "Logs a message to *logger*. 'level' is expanded with 'log-level'. + Example: (log :severe \"Bad argument: \" object)" + [level & strings] + `(. *logger* (log (log-level ~level) (str ~@strings)))) diff --git a/src/clojure/contrib/lazy_seqs/lazy_seqs.clj b/src/clojure/contrib/lazy_seqs/lazy_seqs.clj new file mode 100644 index 00000000..9ffd72f4 --- /dev/null +++ b/src/clojure/contrib/lazy_seqs/lazy_seqs.clj @@ -0,0 +1,90 @@ +;; Copyright (c) Stephen C. Gilardi. All rights reserved. The use and +;; distribution terms for this software are covered by the Common Public +;; License 1.0 (http://opensource.org/licenses/cpl.php) which can be found +;; in the file CPL.TXT at the root of this distribution. By using this +;; software in any fashion, you are agreeing to be bound by the terms of +;; this license. You must not remove this notice, or any other, from this +;; software. +;; +;; lazy-seqs +;; +;; == Lazy sequences == +;; +;; primes - based on the "naive" implemention described in [1] plus a +;; small "wheel" which eliminates multiples of 2, 3, 5, and +;; 7 from consideration by incrementing past them. Also inspired +;; by code from Christophe Grand in [2]. +;; +;; fibs - based on code from Rich Hickey at the Clojure wiki [3] +;; +;; powers-of-2 - all the powers of 2 +;; +;; == Lazy sequence functions == +;; +;; rotations - returns a lazy seq of all the rotations of a seq +;; +;; permutations - returns a lazy seq of all the permutations of a seq +;; +;; [1] http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf +;; [2] http://clj-me.blogspot.com/2008/06/primes.html +;; [3] http://en.wikibooks.org/wiki/Clojure_Programming#Examples +;; +;; scgilardi (gmail) +;; Created 07 June 2008 + +(clojure/in-ns 'clojure.contrib.lazy-seqs) +(clojure/refer 'clojure) + +(clojure.contrib.lib/use '(clojure.contrib def)) + +(defvar primes + (lazy-cat [2 3 5 7] + (let [primes-from + (fn primes-from [n [f & r]] + (if (some #(zero? (rem n %)) + (take-while #(<= (* % %) n) primes)) + (recur (+ n f) r) + (lazy-cons n (primes-from (+ n f) r)))) + wheel (cycle [2 4 2 4 6 2 6 4 2 4 6 6 2 6 4 2 + 6 4 6 8 4 2 4 2 4 8 6 4 6 2 4 6 + 2 6 6 4 2 4 6 2 6 4 2 4 2 10 2 10])] + (primes-from 11 wheel))) + "A lazy sequence of all the prime numbers.") + +(defvar fibs + (lazy-cat [0 1] + (let [rest-fn + (fn rest-fn [a b] + (let [next (+ a b)] + (lazy-cons next (rest-fn b next))))] + (rest-fn 0 1))) + "A lazy sequence of all the fibonacci numbers.") + +(defvar powers-of-2 + (lazy-cons 1 + (let [rest-fn + (fn rest-fn [n] + (let [next (bit-shift-left n 1)] + (lazy-cons next (rest-fn next))))] + (rest-fn 1))) + "A lazy sequence of all the powers of 2") + +(defn rotations + "Returns a lazy seq of all rotations of a seq" + [x] + (if (seq x) + (map + (fn [n _] + (lazy-cat (drop n x) (take n x))) + (iterate inc 0) x) + (list nil))) + +(defn permutations + "Returns a lazy seq of all permutations of a seq" + [x] + (if (seq x) + (mapcat + (fn [[f & r]] + (map #(cons f %) (permutations r))) + (rotations x)) + (list nil))) diff --git a/src/clojure/contrib/lib/lib.clj b/src/clojure/contrib/lib/lib.clj new file mode 100644 index 00000000..c5324b07 --- /dev/null +++ b/src/clojure/contrib/lib/lib.clj @@ -0,0 +1,483 @@ +;; Copyright (c) Stephen C. Gilardi. All rights reserved. The use and +;; distribution terms for this software are covered by the Common Public +;; License 1.0 (http://opensource.org/licenses/cpl.php) which can be found +;; in the file CPL.TXT at the root of this distribution. By using this +;; software in any fashion, you are agreeing to be bound by the terms of +;; this license. You must not remove this notice, or any other, from this +;; software. +;; +;; clojure.contrib.lib (Lib) +;; +;; Lib provides functions for loading and managing Clojure source code +;; contained in Java resources. +;; +;; A 'lib' is a unit of Clojure source code contained in a Java resource +;; and named by a symbol. The lib's name is used to identify the lib and +;; to locate the resource that contains it. +;; +;; Lib provides functions to: +;; +;; - find a resource given a classpath-relative path +;; - load code from a resource given an absolute path +;; - load libs from resources given lib names +;; - load namespace definitions given namespace specs +;; - ensure namespace definitions have been loaded while avoiding +;; duplicate loads, and +;; - create a namespace and refer 'clojure into it succinctly +;; +;; Symbols, Namespaces, Packages +;; +;; Symbols in Clojure are two-part identifiers - with an optional +;; namespace and a name, both strings. Namespaces are used to distinguish +;; two symbols that have the same name. Vars are named by symbols that +;; must have a namespace part, and thus can be considered to be in +;; namespaces. [From Clojure documentation] +;; +;; Packages in Java play a role similar to Clojure namespaces - they +;; partition the global namespace to allow large programs to avoid name +;; conflicts. Java defines a mapping from package names to directories +;; within classpath: components of a package name separated by periods +;; correspond to components of a classpath-relative path separated by +;; slashes. Lib uses this same mapping to locate libs and namespaces in +;; classpath. +;; +;; Loading Libs +;; +;; A lib's name provides the location of the resource that contains it in +;; classpath. The resource name is the last component of the lib's path +;; followed by ".clj". For example, a lib named 'a.b.c is contained in the +;; resource "<classpath>/a/b/c.clj". +;; +;; Loading Namespaces +;; +;; To load a namespace definition, Lib loads the 'root lib' for the +;; namespace. The root lib's name is derived from the namespace name by +;; repeating its last component. For example, the root lib for the +;; namespace 'x.y.z is the lib 'x.y.z.z contained in the resource +;; "<classpath>/x/y/z/z.clj". The namespace definition may be entirely +;; within the root lib or it may span multiple libs in the hierarchy of +;; directories at and under the namespace's directory. In the latter case +;; the root lib must include commands to (directly or indirectly) load the +;; remaining libs. +;; +;; Nsspecs +;; +;; An nsspec specifies a namespace to load. It is either a namespace name +;; or a list containing the namespace name and zero or more options +;; expressed as squential keywords and values. Nsspec examples: +;; - 'clojure.contrib.sql +;; - '(clojure.contrib.sql) +;; - '(clojure.contrib.sql :exclude (get-connection)). +;; - '(clojure.contrib.sql :as sql) +;; +;; Prefix Lists +;; +;; It is common for Clojure code to depend on several libs or namespaces +;; whose names have one or more initial components in common. When +;; specifying lib or namespace names for Lib to use, prefix lists can be +;; used to reduce repetition in the call. A prefix list is a list +;; containing the shared prefix followed by lib or namespace names and/or +;; prefix lists with the shared prefix elided. For example, the following +;; are all equivalent: +;; (load-libs 'a.b.c 'a.b.d 'a.c.e 'a.c.f) ; all names fully qualified +;; (load-libs '(a.b c d) '(a.c e f)) ; 'a.b and 'a.c extracted +;; (load-libs '(a (b c d) (c e f))) ; all common prefixes extracted +;; Symbols containing explicit periods and the equivalent prefix lists may +;; be mixed freely. +;; +;; Lib Functions +;; +;; Resources +;; +;; Function: find-resource +;; Searches available class loaders for a resource, returns URL or nil +;; +;; Function: load-resource +;; Loads Clojure source from an absolute path: URI, URL or String +;; +;; Libs +;; +;; Function: load-libs +;; Loads libs by name from arbitrary locations within classpath +;; +;; Namespaces +;; +;; Function: load-namespaces +;; Loads namespace definitions by loading namespace root libs +;; +;; Function: namespaces +;; Returns a sorted set symbols naming namespaces that have been loaded +;; with the :require option - used to track and avoid duplicate loads. +;; +;; Function: require +;; Loads namespace definitions that have not yet been loaded +;; +;; Function: use +;; Requires namespaces and refers to them using clojure/refer +;; +;; Function: init-ns +;; Creates and enters a namespace and refers to 'clojure and |