在Clojure中更改全局变量的值

时间:2013-02-09 19:55:22

标签: clojure

我在Clojure中是一个完全的开始,我遇到了一个问题,我甚至不确定是否可以在Closure中完成。

所以问题如下。我已经实现了一个从一个区间(最多限度)计算素数的函数。

(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 [])
    )
)

这很好用。但我现在想要的是有一个全局变量应该存储在每个方法调用变量中找到的素数以便稍后查找。在其他语言中,如Python,Ruby或Scala,我只需要通过设置一个Set来添加条目,然后再从函数中退出。但是在Clojure中,我没有想到如何解决这个问题。

基本上我尝试的是,在某个地方宣布:

(def PRIMES_FOUND_SO_FAR #{})

然后以某种方式返回时将条目添加到此变量中。在Clojure中这有可能吗?如果可以的话怎么样?我已尝试使用其他变量来使用swap!atom更改其值,或设置!但无论如何都无法在这里工作。

2 个答案:

答案 0 :(得分:8)

首先,我强烈建议您阅读有关clojure代码约定What are Clojure's Naming Conventions?

的内容

让我向您展示代码的一些改进。

1)应用clojure命名约定。

然后从(+ variable 1)切换到(inc variable)(与dec相同的优化)。

同样(= FIND_MORE_PRIMES false)可以简单地替换为find-more-primes?

最后条件(= 0 smthng)可以用更惯用的风格(zero? smthng)

写出来

现在您的代码看起来更具可读性:

(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)现在我们应该删除冗余的do调用,因为它包装了唯一的一个函数调用。

最后一个技巧是通过将整个gather-primes-in-range调用交换到recur来应用尾递归(http://clojure.org/special_forms#Special%20Forms--(recur%20exprs *))

(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))))

现在是回答你问题的时候了。你不会从这种方法中受益

(def PRIMES_FOUND_SO_FAR #{})

因为您没有机会更改此设置。您可以处理的唯一事情是从那个创建一些新的不可变数据结构。

正如@georgek所说,你可以在这种特殊情况下使用atom

(def PRIMES_FOUND_SO_FAR (atom #{}))

向atom添加新的素数:

(swap! PRIMES_FOUND_SO_FAR conj prime-number)

Deref atom用于提取值:

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

无论如何你的代码有点必要但是你应该永远记住,clojure是具有不可变数据结构的函数式语言,函数作为参数等等,使用全局变量根本不是一个好主意。顺便说一句,这就是你的函数在clojure风格中的样子:

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

答案 1 :(得分:0)

对于那些花了太多时间搜索如何在clojure中修改全局(根)变量的人,这里的解决方案是:

(def user-remote-browser "anonymous")

您可以在我想使用的任何地方进行修改,但使用的名称相同:

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

alter-var-root使用函数来修改变量, 不断创建一个常量函数,在此返回字符串名称