【问题标题】:change values from global variable in Clojure从 Clojure 中的全局变量更改值
【发布时间】:2013-02-09 19:55:22
【问题描述】:

我是 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 来更改其他变量的值,或者设置!但在任何情况下都无法在这里工作。

【问题讨论】:

  • 你是说你没有尝试用原子做这个吗?

标签: clojure


【解决方案1】:

首先,我强烈建议您阅读 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 #{}))

向原子添加新的素数:

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

【讨论】:

  • 谢谢,这帮助了很多人。一个简单的问题,我如何将布尔原子更改为具有新值(如果可能的话)?我想用它来设置全局标志需要更多素数。
  • 定义变量:(def need? (atom false)) 更改它使用(reset! need? true)。获取 atom - @need? 的值。请不要再问这可能吗? Clojure 的魔力比你想象的要多得多。 :)
【解决方案2】:

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

(def user-remote-browser "anonymous")

你可以从我想的任何地方修改它,但在相同的命名空间中:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-11
    • 1970-01-01
    相关资源
    最近更新 更多