提问者:小点点

在Clojure中从全局变量更改值


我是Clojure的完全初学者,我遇到了一个问题,我甚至不确定是否可以在闭包中完成。

所以问题如下。我实现了一个函数,它从一个区间(直到一个极限)计算素数。

(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中完全可能吗?如果是的话,如何实现?我尝试过使用交换!atom或set!来更改其他变量的值。但在任何情况下都无法使其在这里工作。


共2个答案

匿名用户

首先,我强烈建议您阅读Clojure代码约定什么是Clojure的命名约定?

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

1)应用Clojure命名约定。

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

另外(=FIND_MORE_PRIMESfalse)可以简单地替换为find-more质数?

最后,条件(=0 smthng)可以用更惯用的风格编写(零?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调用,因为它包装了唯一的一个函数调用。

最后一个技巧是应用尾递归(http://clojure.org/special_forms#SpecialForms--(recur exprs*)),方法是将整个聚集素数范围内调用交换到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))))

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

(def PRIMES_FOUND_SO_FAR #{})

因为你没有机会改变这个集合。你唯一能处理的就是从那个集合创建一些新的不可变数据结构。

正如@orggek提到的,在这种情况下,你可以简单地使用atom。

(def PRIMES_FOUND_SO_FAR (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是函数式语言,具有不可变的数据结构,函数作为参数等。使用全局变量一点也不好。BTW这就是你的函数在Clojure风格中的样子:

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

匿名用户

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

(def user-remote-browser "anonymous")

你可以从任何地方修改它,我想,但在同一个名称中:

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

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