Greetings, Clojurers! I've been playing with clojure, particularly with lazy sequences. Some of the results have left me puzzled, so I saved a REPL session wherein I illustrate the points of puzzlement. REPL lines are indented below; added comments are unindented.
Clojure 1.2.1 I define a silly version of "even?" which uses a lazy sequence: user=> (defn ev? [n] (nth (cycle [true false]) n)) #'user/ev? It works, albeit slowly, for large arguments: user=> (time (ev? 9876543210)) "Elapsed time: 337225.287 msecs" true This won't work, if any reference is retained to that long sequence: user=> (def ev-stream (cycle [true false])) #'user/ev-stream user=> (defn ev? [n] (nth ev-stream n)) #'user/ev? user=> (time (ev? 9876543210)) Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at java.lang.reflect.Method.copy(Method.java:143) at java.lang.reflect.ReflectAccess.copyMethod(ReflectAccess.java:118) at sun.reflect.ReflectionFactory.copyMethod(ReflectionFactory.java: 282) at java.lang.Class.copyMethods(Class.java:2748) at java.lang.Class.getMethods(Class.java:1410) at clojure.lang.Reflector.getMethods(Reflector.java:310) at clojure.lang.Reflector.invokeInstanceMethod(Reflector.java:27) at clojure.main$repl_caught.invoke(main.clj:116) at clojure.main$repl$fn__5637.invoke(main.clj:206) at clojure.main$repl.doInvoke(main.clj:204) at clojure.lang.RestFn.invoke(RestFn.java:421) at clojure.main$repl_opt.invoke(main.clj:262) at clojure.main$main.doInvoke(main.clj:355) at clojure.lang.RestFn.invoke(RestFn.java:397) at clojure.lang.Var.invoke(Var.java:361) at clojure.lang.AFn.applyToHelper(AFn.java:159) at clojure.lang.Var.applyTo(Var.java:482) at clojure.main.main(main.java:37) Process scheme exited abnormally with code 1 Okay, that was the expected java.lang.barf. No puzzlement so far. Now I restart the REPL. Clojure 1.2.1 Once again, I make a globally referenced, infinitely long stream. But now I use "lazy-seq" instead of "cycle": user=> (def ev-stream (lazy-seq (cons true (cons false ev- stream)))) #'user/ev-stream user=> (defn ev? [n] (nth ev-stream n)) #'user/ev? user=> (time (ev? 9876543210)) "Elapsed time: 47244.061 msecs" true OMG! Not only did it NOT hose the heap and crash, it actually ran much faster than the version with the unreferenced "(cycle [true false])". The only reason I can think of, for this to NOT exhaust memory, is that the lazy-seq macro knows when to construct a circular list. Is that what happens? If so, why DOESN'T it happen with "cycle", where it's obviously the behavior one would want? Okay, now I'll play the same game again, but with "mod 3": user=> (def mod3-stream (lazy-seq (cons 0 (cons 1 (cons 2 mod3- stream))))) #'user/mod3-stream user=> (defn mod3 [n] (nth mod3-stream n)) #'user/mod3 It passes a simple test: user=> (map mod3 (range 20)) (0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1 2 0 1) It passes a test with a pretty big number: user=> (time (mod3 987654321)) "Elapsed time: 28998.966 msecs" 0 user=> (mod 987654321 3) 0 Now I'll test it with 9876543210, a number which "ev?" was able to handle: user=> (time (mod3 9876543210)) "Elapsed time: 37759.615 msecs" 1 user=> (mod 987654321 3) 0 Whoa! The computation finished in reasonable time, but with the WRONG answer! How did that happen? Did I find a bug? Thanks for reading this far, and best regards, George Kangas -- 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 Note that posts from new members are moderated - please be patient with your first post. 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