我是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!来更改其他变量的值。但在任何情况下都无法使其在这里工作。
首先,我强烈建议您阅读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使用函数修改变量,不断创建一个常量函数,在这里返回字符串名称