I'm trying to create an sqrt function using streams in Clojure. For that, I need to define the stream inside the function and return it. The problem resides in that the stream is defined in terms of itself; therefore, using let is not possible, and using def is bogus because it affects the global scope. Is there any way to simulate using a def inside a function that doesn't affect the global scope?
(defmacro cons-stream [a b]
(list 'lazy-seq (list 'cons a (list 'lazy-seq b))))
(defn stream-car [stream] (first stream))
(defn stream-cdr [stream] (rest stream))
(defn stream-map [proc & streams]
(if (empty? (first streams))
'()
(cons-stream (apply proc (map stream-car streams))
(apply stream-map proc (map stream-cdr streams)))))
(defn average [a b] (/ (+ a b) 2.0))
(defn sqrt-improve [guess x]
(average guess (/ x guess)))
(defn sqrt-stream [x]
(def guesses (cons-stream 1.0
(stream-map #(sqrt-improve % x) guesses)))
guesses)
I don't want sqrt-stream to create a global guesses stream.
EDIT
In clojure the let definition is only available when it has been evaluated. So this definition throws an error.
(defn sqrt-stream [x]
(let [guesses (cons-stream 1.0
(stream-map #(sqrt-improve % x) guesses))]
guesses))
@CharlesDuffy is right. A promise
can be used here:
(defn sqrt-stream [x]
(let [p (promise)]
(deliver p (cons-stream 1.0
(stream-map #(sqrt-improve % x) @p)))
@p))
This does appear to be an XY problem though. Just use existing constructs (as shown below in my previous answer). Also note, stream-map
is just the built in map
, and you can use a syntax quote (`
) to neaten up cons-stream
:
(defmacro cons-stream [a b]
`(lazy-seq (cons ~a (lazy-seq ~b)))) ; ~ unquotes a and b
(defn average [a b]
(/ (+ a b) 2.0))
(defn sqrt-improve [guess x]
(average guess (/ x guess)))
(defn sqrt-stream [x]
(let [p (promise)]
(deliver p (cons-stream 1.0
(map #(sqrt-improve % x) @p)))
@p))
I suggest using iterate
here though. It repeatedly applies a function to an initial value, and returns an infinite lazy list of the results. This has the same effect as your original code, but relies entirely on core
constructs:
(defn average [a b]
(/ (+ a b) 2.0))
(defn sqrt-improve [guess x]
(average guess (/ x guess)))
(defn sqrt-stream [n]
(iterate #(sqrt-improve % n) ; Apply this function over and over again
1.0)) ; The initial value to iterate over
Then, use it like:
(->> (sqrt-stream 10)
(take 10))
=>
(1.0
5.5
3.659090909090909
3.196005081874647
3.16245562280389
3.162277665175675
3.162277660168379
3.162277660168379
3.162277660168379
3.162277660168379)
You can get a final answer from this by taking as many results as you want to achieve a desired accuracy, then grabbing the final (last
) value:
(->> (sqrt-stream 10)
(take 100)
(last))
=> 3.162277660168379