Bogdan
Bogdan

Reputation: 8246

change values from global variable in Clojure

I'm a total beginer in Clojure and I've ran into a problem that I'm not even sure if can be done in Closure.

So the issue is the following. I've implemented a function that computes the prime numbers from an interval (up to a limit).

(defn gather_primes_in_range [range_start range_end target_number prime_list]
    (if (or (= 0 target_number) (> range_start range_end) (= FIND_MORE_PRIMES false)) 
        prime_list
        (do
            (if (is_prime? range_start)
                (gather_primes_in_range (+ range_start 1) range_end (- target_number 1) (conj, prime_list, range_start))
                (gather_primes_in_range (+ range_start 1) range_end target_number prime_list)
            )
        )
    )
)

(defn find_nr_of_primes_in_range [range_start range_end target_number]
    (if (< range_start 2)
        (gather_primes_in_range 2 range_end target_number [])
        (gather_primes_in_range range_start range_end target_number [])
    )
)

This works just fine. But what I want now is to have a global variable that should store on each method call the primes that are found in a variable to lookup later. In other languages like Python, Ruby or Scala I would just do this by having a Set to which I add entries before returing from the function. But in Clojure I have no ideea how to go around this.

Basically what I tried is, have somewhere global declared:

(def PRIMES_FOUND_SO_FAR #{})

And then somehow on return add the entries to this variable. Is this possible at all in Clojure and if so how? I've tried on other variables to change their values using either swap! and atom, or set! but could not make it to work here in any situation.

Upvotes: 2

Views: 4264

Answers (2)

Damien Mattei
Damien Mattei

Reputation: 384

for those who have spent too much time searching how to modify a global (root) variable in clojure here is the solution:

(def user-remote-browser "anonymous")

you can modify it from anywhere i suppose but in the same namepace with:

(alter-var-root #'user-remote-browser (constantly name))

alter-var-root use a function to modify a variable, constantly create a constant function returning here the string name

Upvotes: 1

hsestupin
hsestupin

Reputation: 1115

Firstly, I strongly advice you to read about clojure code conventions What are Clojure's Naming Conventions?

Let me show you some improvements of your code.

1) Applying clojure naming conventions.

Then switch from (+ variable 1) to (inc variable) (the same optimizations with dec).

Also (= FIND_MORE_PRIMES false) can be simply replaced by find-more-primes?

And finally the condition (= 0 smthng) could be written in more idiomatic style (zero? smthng)

Now your code looks a bit more readable:

(defn gather-primes-in-range [range-start range-end target-number prime-list]
  (if (or (zero? target-number) (> range-start range-end) need-more-primes?)
    prime-list
    (do
      (if (is-prime? range-start)
        (gather-primes-in-range (inc range-start) range-end (dec target-number) (conj prime-list range-start))
        (gather-primes-in-range (inc range-start) range-end target-number prime-list)))))

2) Now we should remove redundant do call cause it wraps the only one function call.

And the last trick is to apply tail recursion (http://clojure.org/special_forms#Special%20Forms--(recur%20exprs*)) via swapping entire gather-primes-in-range calls to recur

(defn gather-primes-in-range 
  [range-start range-end target-number prime-list]
    (if (or (zero? target-number) (> range-start range-end) need-more-primes?)
      prime-list
      (if (is-prime? range-start)
        (recur (inc range-start) range-end (dec target-number) (conj prime-list range-start))
        (recur (inc range-start) range-end target-number prime-list))))

And here comes time for answering your question. You wouldn't benefit from this approach

(def PRIMES_FOUND_SO_FAR #{})

because you haven't opportunity to change this set. The only thing that you can deal with it is to create some new immutable data structure from that one.

As @georgek mention you could simply use atom in this particular case.

(def PRIMES_FOUND_SO_FAR (atom #{}))

Adding new prime number to atom:

(swap! PRIMES_FOUND_SO_FAR conj prime-number)

Deref atom for extracting the value:

@PRIMES_FOUND_SO_FAR ;; or (deref PRIMES_FOUND_SO_FAR)
-> #{2 3 5 7 11}

Anyway your code is a little bit imperative but you should always remember that clojure is functional language with immutable data structures, functions as arguments, etc. using global variables is not good idea at all. BTW thats how your function should look like in clojure style:

(defn gather-primes-in-range [start end target-number]
  (take target-number (filter is-prime? (range start end))))

Upvotes: 9

Related Questions