idiomatic clojure range/collision checking? - clojure

getting back into clojure, and i wrote this little thing to check if two boxes are colliding by checking if vertices of one are inside the other:
(defn around
[val radius]
(let [half (/ radius 2)
low (- val half)
high (+ val half)]
[low high]))
(defn colliding?
[this that]
(let [[this-x1 this-x2] (around (:x this) (:w this))
[this-y1 this-y2] (around (:y this) (:h this))
[this-z1 this-z2] (around (:z this) (:l this))
[that-x1 that-x2] (around (:x that) (:w that))
[that-y1 that-y2] (around (:y that) (:h that))
[that-z1 that-z2] (around (:z that) (:l that))]
(or (and (or (<= that-x1 this-x1 that-x2)
(<= that-x1 this-x2 that-x2))
(or (<= that-y1 this-y1 that-y2)
(<= that-y1 this-y2 that-y2))
(or (<= that-z1 this-z1 that-z2)
(<= that-z1 this-z2 that-z2)))
(and (or (<= this-x1 that-x1 this-x2)
(<= this-x1 that-x2 this-x2))
(or (<= this-y1 that-y1 this-y2)
(<= this-y1 that-y2 this-y2))
(or (<= this-z1 that-z1 this-z2)
(<= this-z1 that-z2 this-z2))))))
this smells pretty bad due to repetition but i'm not sure what the best approach is for cleaning this up. is there a better way to do this?

You can simplify the code by using the fact that there is a collision in one direction unless the starting coordinate of one object is higher than the ending coordinate of the other.
I.e. a sufficient test is
(not (or (> this-x1 that-x2)
(> that-x1 this-x2)))
which is equivalent to
(and (<= this-x1 that-x2)
(<= that-x1 this-x2))
Using this, your colliding? may be simplified to
(defn colliding?
[this that]
(let [[this-x1 this-x2] (around (:x this) (:w this))
[this-y1 this-y2] (around (:y this) (:h this))
[this-z1 this-z2] (around (:z this) (:l this))
[that-x1 that-x2] (around (:x that) (:w that))
[that-y1 that-y2] (around (:y that) (:h that))
[that-z1 that-z2] (around (:z that) (:l that))]
(and (<= this-x1 that-x2)
(<= that-x1 this-x2)
(<= this-y1 that-y2)
(<= that-y1 this-y2)
(<= this-z1 that-z2)
(<= that-z1 this-z2))))
If you then factor out a function conflicting? that checks for overlapping in one dimension,
(defn conflicting?
[this that coordinate size]
(let [[this-c1 this-c2] (around (coordinate this) (size this))
[that-c1 that-c2] (around (coordinate that) (size that))]
(and (<= this-c1 that-c2)
(<= that-c1 this-c2)))
colliding? can be further simplified by utilizing mapping over dimensions and sizes:
(defn colliding?
[this that]
(every? true? (map #(conflicting? this that %1 %2)
[:x :y :z]
[:w :h :l])))
Edit:
Furthermore, conflicting? may be simplified to
(defn conflicting?
[this that coordinate size]
(<= (math/abs (- (coordinate this) (coordinate that)))
(/ (+ (size this) (size that)) 2)))
making the function around obsolete for the purpose of detecting collisions.

Related

Clojure - sum up a bunch of numbers

Hey I'm doing a Project Euler question, and I'm looking to sum up all the numbers under 1000 that are multiplies of 3 or 5.
But being a clojure noob, my code just keeps returning zero.. and I'm not sure why.
(defn sum-of-multiples [max]
(let [result (atom 0)]
(for [i (range max)]
(if (or (= (rem i 3) 0) (= (rem i 5) 0))
(swap! result (+ #result i)))
)
#result))
(sum-of-multiples 1000)
Also the line (swap! result (+ #result i))) bugs me.. In C# I could do result += i, but I'm guessing there must be a better way to this in Clojure?
In Clojure - and at large in functional programming - we avoid assignment as it destroys state history and makes writing concurrent programs a whole lot harder. In fact, Clojure doesn't even support assignment. An atom is a reference type that is thread safe.
Another common trait of functional programming is that we try to solve problems as a series of data transformations. In your case you case some data, a list of numbers from 0 to 1000 exclusive, and you need to obtain the sum of all numbers that match a predicate. This can certainly be done by applying data transformations and completely removing the need for assignment. One such implementation is this:
(->> (range 1000)
(filter #(or (= (rem % 3) 0) (= (rem % 5) 0)))
(reduce +))
Please understand that a function such as the one you wrote isn't considered idiomatic code. Having said that, in the interest of learning, it can be made to work like so:
(defn sum-of-multiples [max]
(let [result (atom 0)]
(doseq [i (range max)]
(if (or (= (rem i 3) 0) (= (rem i 5) 0))
(swap! result #(+ % i)))
)
#result))
(sum-of-multiples 1000)
for returns a lazy sequence but since you're simply interested in the side-effects caused by swap! you need to use doseq to force the sequence. The other problem is that the second argument to swap! is a function, so you don't need to deref result again.
for is a list comprehension that return a lazy sequence, you have to traverse it for your code to work:
(defn sum-of-multiples [max]
(let [result (atom 0)]
(dorun
(for [i (range max)]
(if (or (= (rem i 3) 0) (= (rem i 5) 0))
(swap! result + i))))
#result))
An equivalent, more idiomatic implementation using for:
(defn sum-of-multiples [max]
(reduce +
(for [i (range max)
:when (or (zero? (rem i 3))
(zero? (rem i 5)))]
i)))
The other answers are good examples of what I alluded to in my comment. For the sake of completeness, here's a solution that uses loop/recur, so it may be easier to understand for someone who's still not comfortable with concepts like filter, map or reduce. It also happens to be about 30-40% faster, not that it really matters in this case.
(defn sum-of-multiples [max]
(loop [i 0
sum 0]
(if (> max i)
(recur (inc i)
(if (or (zero? (rem i 3)) (zero? (rem i 5)))
(+ sum i)
sum))
sum)))

Quicksort in Clojure

I am trying to prove Clojure performance can be on equal footing with Java. An important use case I've found is the Quicksort. I have written an implementation as follows:
(set! *unchecked-math* true)
(defn qsort [^longs a]
(let [qs (fn qs [^long low, ^long high]
(when (< low high)
(let [pivot (aget a low)
[i j]
(loop [i low, j high]
(let [i (loop [i i] (if (< (aget a i) pivot)
(recur (inc i)) i))
j (loop [j j] (if (> (aget a j) pivot)
(recur (dec j)) j))
[i j] (if (<= i j)
(let [tmp (aget a i)]
(aset a i (aget a j)) (aset a j tmp)
[(inc i) (dec j)])
[i j])]
(if (< i j) (recur i j) [i j])))]
(when (< low j) (qs low j))
(when (< i high) (qs i high)))))]
(qs 0 (dec (alength a))))
a)
Also, this helps call the Java quicksort:
(defn jqsort [^longs a] (java.util.Arrays/sort a) a))
Now, for the benchmark.
user> (def xs (let [rnd (java.util.Random.)]
(long-array (repeatedly 100000 #(.nextLong rnd)))))
#'user/xs
user> (def ys (long-array xs))
#'user/ys
user> (time (qsort ys))
"Elapsed time: 163.33 msecs"
#<long[] [J#3ae34094>
user> (def ys (long-array xs))
user> (time (jqsort ys))
"Elapsed time: 13.895 msecs"
#<long[] [J#1b2b2f7f>
Performance is worlds apart (an order of magnitude, and then some).
Is there anything I'm missing, any Clojure feature I may have used? I think the main source of performance degradation is when I need to return several values from a loop and must allocate a vector for that. Can this be avoided?
BTW running Clojure 1.4. Also note that I have run the benchmark multiple times in order to warm up the HotSpot. These are the times when they settle down.
Update
The most terrible weakness in my code is not just the allocation of vectors, but the fact that they force boxing and break the primitive chain. Another weakness is using results of loop because they also break the chain. Yep, performance in Clojure is still a minefield.
This version is based on #mikera's, is just as fast and doesn't require the use of ugly macros. On my machine this takes ~12ms vs ~9ms for java.util.Arrays/sort:
(set! *unchecked-math* true)
(set! *warn-on-reflection* true)
(defn swap [^longs a ^long i ^long j]
(let [t (aget a i)]
(aset a i (aget a j))
(aset a j t)))
(defn ^long apartition [^longs a ^long pivot ^long i ^long j]
(loop [i i j j]
(if (<= i j)
(let [v (aget a i)]
(if (< v pivot)
(recur (inc i) j)
(do
(when (< i j)
(aset a i (aget a j))
(aset a j v))
(recur i (dec j)))))
i)))
(defn qsort
([^longs a]
(qsort a 0 (long (alength a))))
([^longs a ^long lo ^long hi]
(when
(< (inc lo) hi)
(let [pivot (aget a lo)
split (dec (apartition a pivot (inc lo) (dec hi)))]
(when (> split lo)
(swap a lo split))
(qsort a lo split)
(qsort a (inc split) hi)))
a))
(defn ^longs rand-long-array []
(let [rnd (java.util.Random.)]
(long-array (repeatedly 100000 #(.nextLong rnd)))))
(comment
(dotimes [_ 10]
(let [as (rand-long-array)]
(time
(dotimes [_ 1]
(qsort as)))))
)
The need for manual inlining is mostly unnecessary starting with Clojure 1.3. With a few type hints only on the function arguments the JVM will do the inlining for you. There is no need to cast index arguments to int for the the array operations - Clojure does this for you.
One thing to watch out for is that nested loop/recur does present problems for JVM inlining since loop/recur doesn't (at this time) support returning primitives. So you have to break apart your code into separate fns. This is for the best as nested loop/recurs get very ugly in Clojure anyhow.
For a more detailed look on how to consistently achieve Java performance (when you actually need it) please examine and understand test.benchmark.
This is slightly horrific because of the macros, but with this code I think you can match the Java speed (I get around 11ms for the benchmark):
(set! *unchecked-math* true)
(defmacro swap [a i j]
`(let [a# ~a
i# ~i
j# ~j
t# (aget a# i#)]
(aset a# i# (aget a# j#))
(aset a# j# t#)))
(defmacro apartition [a pivot i j]
`(let [pivot# ~pivot]
(loop [i# ~i
j# ~j]
(if (<= i# j#)
(let [v# (aget ~a i#)]
(if (< v# pivot#)
(recur (inc i#) j#)
(do
(when (< i# j#)
(aset ~a i# (aget ~a j#))
(aset ~a j# v#))
(recur i# (dec j#)))))
i#))))
(defn qsort
([^longs a]
(qsort a 0 (alength a)))
([^longs a ^long lo ^long hi]
(let [lo (int lo)
hi (int hi)]
(when
(< (inc lo) hi)
(let [pivot (aget a lo)
split (dec (apartition a pivot (inc lo) (dec hi)))]
(when (> split lo) (swap a lo split))
(qsort a lo split)
(qsort a (inc split) hi)))
a)))
The main tricks are:
Do everything with primitive arithmetic
Use ints for the array indexes (this avoids some unnecessary casts, not a big deal but every little helps....)
Use macros rather than functions to break up the code (avoids function call overhead and parameter boxing)
Use loop/recur for maximum speed in the inner loop (i.e. partitioning the subarray)
Avoid constructing any new objects on the heap (so avoid vectors, sequences, maps etc.)
The Joy of Clojure, Chapter 6.4 describes a lazy quicksort algorithm.The beauty of lazy sorting is that it will only do as much work as necessary to find the first x values. So if x << n this algorithm is O(n).
(ns joy.q)
(defn sort-parts
"Lazy, tail-recursive, incremental quicksort. Works against
and creates partitions based on the pivot, defined as 'work'."
[work]
(lazy-seq
(loop [[part & parts] work]
(if-let [[pivot & xs] (seq part)]
(let [smaller? #(< % pivot)]
(recur (list*
(filter smaller? xs)
pivot
(remove smaller? xs)
parts)))
(when-let [[x & parts] parts]
(cons x (sort-parts parts)))))))
(defn qsort [xs]
(sort-parts (list xs)))
By examining the main points from mikera's answer, you can see that they are mostly focused on eliminating the overhead introduced by using idiomatic (as opposed to tweaked) Clojure, which would probably not exist in an idiomatic Java implementation:
primitive arithmetic - slightly easier and more idiomatic in Java, you are more likely to use ints than Integers
ints for the array indexes - the same
Use macros rather than functions to break up the code (avoids functional call overhead and boxing) - fixes a problem introduced by using the language. Clojure encourages functional style, hence a function call overhead (and boxing).
Use loop/recur for maximum speed in the inner loop - in Java you'd idiomatically use an ordinary loop (which is what loop/recur compiles to anyway, as far as I know)
That being said, there actually is another trivial solution. Write (or find) an efficient Java implementation of Quick Sort, say something with a signature like this:
Sort.quickSort(long[] elems)
And then call it from Clojure:
(Sort/quickSort elems)
Checklist:
as efficient as in Java - yes
idiomatic in Clojure - arguably yes, I'd say that Java-interop is one of Clojure's core features.
reusable - yes, there's a good chance that you can easily find a very efficient Java implementation already written.
I'm not trying to troll, I understand what you are trying to find out with these experiments I'm just adding this answer for the sake of completeness. Let's not overlook the obvious one! :)

How Would You Write This C++ Loop in Clojure?

Although I've done some small amount of programming in functional languages before, I've just started playing with Clojure. Since doing the same kind of "Hello World" programs gets old when learning a new language, I decided to go through the Cinder "Hello, Cinder" tutorial, translating it to Clojure and Quil along the way. In Chapter 5 of the tutorial, you come across this C++ snippet to calculate acceleration for a list of particles:
void ParticleController::repulseParticles() {
for( list<Particle>::iterator p1 = mParticles.begin(); p1 != mParticles.end(); ++p1 ) {
list<Particle>::iterator p2 = p1;
for( ++p2; p2 != mParticles.end(); ++p2 ) {
Vec2f dir = p1->mLoc - p2->mLoc;
float distSqrd = dir.lengthSquared();
if( distSqrd > 0.0f ){
dir.normalize();
float F = 1.0f/distSqrd;
p1->mAcc += dir * ( F / p1->mMass );
p2->mAcc -= dir * ( F / p2->mMass );
}
}
}
}
In my eyes, this code has one very important characteristic: it is doing comparisons between pairs of particles and updating both particles and then skipping the same combination in the future. This is very important for performance reasons, since this piece of code is executed once every frame and there are potentially thousands of particles on screen at any given time (someone who understands big O better than I do can probably tell you the difference between this method and iterating over every combination multiple times).
For reference, I'll show what I came up with. You should notice that the code below only updates one particle at a time, so I'm doing a lot of "extra" work comparing the same particles twice. (Note: some methods left out for brevity, such as "normalize"):
(defn calculate-acceleration [particle1 particle2]
(let [x-distance-between (- (:x particle1) (:x particle2))
y-distance-between (- (:y particle1) (:y particle2))
distance-squared (+ (* x-distance-between x-distance-between) (* y-distance-between y-distance-between))
normalized-direction (normalize x-distance-between y-distance-between)
force (if (> distance-squared 0) (/ (/ 1.0 distance-squared) (:mass particle1)) 0)]
{:x (+ (:x (:accel particle1)) (* (first normalized-direction) force)) :y (+ (:y (:accel particle1)) (* (second normalized-direction) force))}))
(defn update-acceleration [particle particles]
(assoc particle :accel (reduce #(do {:x (+ (:x %) (:x %2)) :y (+ (:y %) (:y %2))}) {:x 0 :y 0} (for [p particles :when (not= particle p)] (calculate-acceleration particle p)))))
(def particles (map #(update-acceleration % particles) particles))
Update: So here's what I ultimately came up with, in case anyone is interested:
(defn get-new-accelerations [particles]
(let [particle-combinations (combinations particles 2)
new-accelerations (map #(calculate-acceleration (first %) (second %)) particle-combinations)
new-accelerations-grouped (for [p particles]
(filter #(not (nil? %))
(map
#(cond (= (first %) p) %2
(= (second %) p) (vec-scale %2 -1))
particle-combinations new-accelerations)))]
(map #(reduce (fn [accum accel] (if (not (nil? accel)) (vec-add accel accum))) {:x 0 :y 0} %)
new-accelerations-grouped)))
Essentially, the process goes something like this:
particle-combinations: Calculate all combinations of particles using the combinatorics "combinations" function
new-accelerations: Calculate a list of accelerations based on the list of combinations
new-accelerations-grouped: Group up the accelerations for each particle (in order) by looping over every particle and checking the list of combinations, building a list of lists where each sub-list is all of the individual accelerations; there's also the subtlety that if the particle is the first entry in the combination list, it gets the original acceleration, but if it's the second, it gets the opposite acceleration. It then filters out nils
Reduce each sub-list of accelerations to the sum of those accelerations
The question now is, is this any faster than what I was doing before? (I haven't tested it yet, but my initial guess is no way).
Update 2:
Here's another version I came up with. I think this version is much better in all respects than the one I posted above: it uses a transient data structure for performance/easy mutability of the new list, and uses loop/recur. It should be much faster than the example I posted above but I haven't tested yet to verify.
(defn transient-particle-accelerations [particles]
(let [num-of-particles (count particles)]
(loop [i 0 new-particles (transient particles)]
(if (< i (- num-of-particles 1))
(do
(loop [j (inc i)]
(if (< j num-of-particles)
(let [p1 (nth particles i)
p2 (nth particles j)
new-p1 (nth new-particles i)
new-p2 (nth new-particles j)
new-acceleration (calculate-acceleration p1 p2)]
(assoc! new-particles i (assoc new-p1 :accel (vec-add (:accel new-p1) new-acceleration)))
(assoc! new-particles j (assoc new-p2 :accel (vec-add (:accel new-p2) (vec-scale new-acceleration -1))))
(recur (inc j)))))
(recur (inc i) new-particles))
(persistent! new-particles)))))
Re-def-ing particles when you want to update them doesn't seem quite right -- I'm guessing that using a ref to store the state of the world, and then updating that ref between cycles, would make more sense.
Down to the algorithmic issue, to me like this is a use case for clojure.math.combinatorics. Something like the following:
(require '[clojure.math.combinatorics :as combinatorics])
(defn update-particles [particles]
(apply concat
(for [[p1 p2] (combinatorics/combinations particles 2)
:let [x-distance-between (- (:x p1) (:x p2))
y-distance-between (- (:y p1) (:y p2))
distance-squared (+ (* x-distance-between x-distance-between)
(* y-distance-between y-distance-between))
normalized-direction (normalize x-distance-between y-distance-between)
p1-force (if (> distance-squared 0)
(/ (/ 1.0 distance-squared) (:mass p1))
0)]]
[{:x (+ (:x (:accel p1)) (* (first normalized-direction) p1-force))
:y (+ (:y (:accel p1)) (* (first normalized-direction) p1-force))}
{:x (+ (:x (:accel p2)) (* (first normalized-direction) p2-force))
:y (+ (:y (:accel p2)) (* (first normalized-direction) p2-force))}]))
...you'll still need the reduce, but this way we're pulling updated values for both particles out of the loop.
So, essentially, you want to select all subsets of size two, then operate on each such pair?
Here's a combinatorics library http://richhickey.github.com/clojure-contrib/combinatorics-api.html with
combinations
function
Usage: (combinations items n) All the unique
ways of taking n different elements from items
Use that to generate your list, then iterate over that.

Binary search in clojure (implementation / performance)

I wrote a binary search function as part of a larger program, but it seems to be slower than it should be and profiling shows a lot of calls to methods in clojure.lang.Numbers.
My understanding is that Clojure can use primitives when it can determine that it can do so. The calls to the methods in clojure.lang.Numbers seems to indicate that it's not using primitives here.
If I coerce the loop variables to ints, it properly complains that the recur arguments are not primitive. If i coerce those too, the code works again but again it's slow. My only guess is that (quot (+ low-idx high-idx) 2) is not producing a primitive but I'm not sure where to go from here.
This is my first program in Clojure so feel free to let me know if there are more cleaner/functional/Clojure ways to do something.
(defn binary-search
[coll coll-size target]
(let [cnt (dec coll-size)]
(loop [low-idx 0 high-idx cnt]
(if (> low-idx high-idx)
nil
(let [mid-idx (quot (+ low-idx high-idx) 2) mid-val (coll mid-idx)]
(cond
(= mid-val target) mid-idx
(< mid-val target) (recur (inc mid-idx) high-idx)
(> mid-val target) (recur low-idx (dec mid-idx))
))))))
(defn binary-search-perf-test
[test-size]
(do
(let [test-set (vec (range 1 (inc test-size))) test-set-size (count test-set)]
(time (count (map #(binary-search2 test-set test-set-size %) test-set)))
)))
First of all, you can use the binary search implementation provided by java.util.Collections:
(java.util.Collections/binarySearch [0 1 2 3] 2 compare)
; => 2
If you skip the compare, the search will be faster still, unless the collection includes bigints, in which case it'll break.
As for your pure Clojure implementation, you can hint coll-size with ^long in the parameter vector -- or maybe just ask for the vector's size at the beginning of the function's body (that's a very fast, constant time operation), replace the (quot ... 2) call with (bit-shift-right ... 1) and use unchecked math for the index calculations. With some additional tweaks a binary search could be written as follows:
(defn binary-search
"Finds earliest occurrence of x in xs (a vector) using binary search."
([xs x]
(loop [l 0 h (unchecked-dec (count xs))]
(if (<= h (inc l))
(cond
(== x (xs l)) l
(== x (xs h)) h
:else nil)
(let [m (unchecked-add l (bit-shift-right (unchecked-subtract h l) 1))]
(if (< (xs m) x)
(recur (unchecked-inc m) h)
(recur l m)))))))
This is still noticeably slower than the Java variant:
(defn java-binsearch [xs x]
(java.util.Collections/binarySearch xs x compare))
binary-search as defined above seems to take about 25% more time than this java-binsearch.
in Clojure 1.2.x you can only coerce local variables and they can't cross functions calls.
starting in Clojure 1.3.0 Clojure can use primative numbers across function calls but not through Higher Order Functions such as map.
if you are using clojure 1.3.0+ then you should be able to accomplish this using type hints
as with any clojure optimization problem the first step is to turn on (set! *warn-on-reflection* true) then add type hints until it no longer complains.
user=> (set! *warn-on-reflection* true)
true
user=> (defn binary-search
[coll coll-size target]
(let [cnt (dec coll-size)]
(loop [low-idx 0 high-idx cnt]
(if (> low-idx high-idx)
nil
(let [mid-idx (quot (+ low-idx high-idx) 2) mid-val (coll mid-idx)]
(cond
(= mid-val target) mid-idx
(< mid-val target) (recur (inc mid-idx) high-idx)
(> mid-val target) (recur low-idx (dec mid-idx))
))))))
NO_SOURCE_FILE:23 recur arg for primitive local: low_idx is not matching primitive,
had: Object, needed: long
Auto-boxing loop arg: low-idx
#'user/binary-search
user=>
to remove this you can type hint the coll-size argument
(defn binary-search
[coll ^long coll-size target]
(let [cnt (dec coll-size)]
(loop [low-idx 0 high-idx cnt]
(if (> low-idx high-idx)
nil
(let [mid-idx (quot (+ low-idx high-idx) 2) mid-val (coll mid-idx)]
(cond
(= mid-val target) mid-idx
(< mid-val target) (recur (inc mid-idx) high-idx)
(> mid-val target) (recur low-idx (dec mid-idx))
))))))
it is understandably difficult to connect the auto-boxing on line 10 to the coll-size parameter because it goes through cnt then high-idx then mid-ixd and so on, so I generally approach these problems by type-hinting everything until I find the one that makes the warnings go away, then remove hints so long as they stay gone

Project Euler #14 and memoization in Clojure

As a neophyte clojurian, it was recommended to me that I go through the Project Euler problems as a way to learn the language. Its definitely a great way to improve your skills and gain confidence. I just finished up my answer to problem #14. It works fine, but to get it running efficiently I had to implement some memoization. I couldn't use the prepackaged memoize function because of the way my code was structured, and I think it was a good experience to roll my own anyways. My question is if there is a good way to encapsulate my cache within the function itself, or if I have to define an external cache like I have done. Also, any tips to make my code more idiomatic would be appreciated.
(use 'clojure.test)
(def mem (atom {}))
(with-test
(defn chain-length
([x] (chain-length x x 0))
([start-val x c]
(if-let [e (last(find #mem x))]
(let [ret (+ c e)]
(swap! mem assoc start-val ret)
ret)
(if (<= x 1)
(let [ret (+ c 1)]
(swap! mem assoc start-val ret)
ret)
(if (even? x)
(recur start-val (/ x 2) (+ c 1))
(recur start-val (+ 1 (* x 3)) (+ c 1)))))))
(is (= 10 (chain-length 13))))
(with-test
(defn longest-chain
([] (longest-chain 2 0 0))
([c max start-num]
(if (>= c 1000000)
start-num
(let [l (chain-length c)]
(if (> l max)
(recur (+ 1 c) l c)
(recur (+ 1 c) max start-num))))))
(is (= 837799 (longest-chain))))
Since you want the cache to be shared between all invocations of chain-length, you would write chain-length as (let [mem (atom {})] (defn chain-length ...)) so that it would only be visible to chain-length.
In this case, since the longest chain is sufficiently small, you could define chain-length using the naive recursive method and use Clojure's builtin memoize function on that.
Here's an idiomatic(?) version using plain old memoize.
(def chain-length
(memoize
(fn [n]
(cond
(== n 1) 1
(even? n) (inc (chain-length (/ n 2)))
:else (inc (chain-length (inc (* 3 n))))))))
(defn longest-chain [start end]
(reduce (fn [x y]
(if (> (second x) (second y)) x y))
(for [n (range start (inc end))]
[n (chain-length n)])))
If you have an urge to use recur, consider map or reduce first. They often do what you want, and sometimes do it better/faster, since they take advantage of chunked seqs.
(inc x) is like (+ 1 x), but inc is about twice as fast.
You can capture the surrounding environment in a clojure :
(defn my-memoize [f]
(let [cache (atom {})]
(fn [x]
(let [cy (get #cache x)]
(if (nil? cy)
(let [fx (f x)]
(reset! cache (assoc #cache x fx)) fx) cy)))))
(defn mul2 [x] (do (print "Hello") (* 2 x)))
(def mmul2 (my-memoize mul2))
user=> (mmul2 2)
Hello4
user=> (mmul2 2)
4
You see the mul2 funciton is only called once.
So the 'cache' is captured by the clojure and can be used to store the values.