【问题标题】:Maintaining state within a recursive function in Clojure在 Clojure 的递归函数中维护状态
【发布时间】:2014-10-01 22:09:02
【问题描述】:

我有一个递归函数,每次调用它都会吐出一个哈希值。

第一次循环哈希是:

{1 "mary", 2 "dean"}

下一轮出局

{23 "ava", 4 "scout"}

最后一轮回归

 {3 "bina", 16 "bob"}

我的函数将始终返回最后一轮数据,{3 "gina", 16 "bob"}。 我不想吐出最后一条数据,而是将它们全部存储在一个巨大的哈希中,以便我可以比较它们。在我比较它们之后,函数应该返回“ava”,因为这是与最高键关联的值。解决这个问题的最佳方法是什么?

【问题讨论】:

  • 你尝试了什么?请给我们一些代码。

标签: recursion clojure state sortedmap


【解决方案1】:

一种通用模式是向递归函数添加状态参数,并提供添加初始状态值的函数的数量。然后在基本情况下,您可以对状态值进行后处理。

这是一个将第一个奇数之前的输入映射到随机值的示例:

user> (defn example
        ([input] (example input {}))  ;; one argument recurs with default value
        ([input state]                ;; two arg case passes the state.
           (if (odd? (first input))
             state
             (recur (rest input)
                    (assoc state (first input) (rand-int 10))))))
#'user/example

从结果状态中选择最大值:

user> (example [2 4 8 9])
{8 0, 4 1, 2 4}
user> (defn example
        ([input] (example input {}))
        ([input state]
            (if (odd? (first input))
             (first (sort-by val state))
             (recur (rest input)
                    (assoc state (first input) (rand-int 10))))))
#'user/example
user> (example [2 4 8 9])
[8 6]

【讨论】:

    【解决方案2】:

    如果您有一个输出一系列值的副作用函数,请将其建模为一个序列。如果序列终止,则该函数可能返回无效值。

    功能

    (defn ensequence [f! valid?]
      (take-while valid? (repeatedly f!)))
    

    ... 返回由副作用函数f! 生成的值序列,每当valid? 测试失败时终止。

    例如,

    (ensequence #(rand-int 10) #(not= 5 %))
    

    ...返回(range 10) 中的随机值序列,在第一个5 之前停止:

    (6 9)
    

    ...例如(您的里程有所不同)。

    为了说明ensequence 在您的情况下如何工作,我们使用了一个逆函数,将序列转换为返回其连续元素的函数,然后nil

    (defn oracle! [coll]
      (let [s (atom coll)]
        (fn [] (let [x (first @s)] (swap! s rest) x))))
    

    例如,

    (repeatedly 10 (oracle! (range 5)))
    ;(0 1 2 3 4 nil nil nil nil nil)
    

    为了您的数据

    (def data [{1 "mary", 2 "dean"} {23 "ava", 4 "scout"} {3 "bina", 16 "bob"}])
    

    功能

    (oracle! data)
    

    ...依次返回它的元素,后面跟着nils:

    (repeatedly 10 (oracle! data))
    ;({1 "mary", 2 "dean"} {23 "ava", 4 "scout"} {3 "bina", 16 "bob"}
       nil nil nil nil nil nil nil) 
    

    我们可以在上面使用ensequence,不管它是如何生成的,来恢复原始序列:

    (ensequence (oracle! data) identity)
    ;({1 "mary", 2 "dean"} {23 "ava", 4 "scout"} {3 "bina", 16 "bob"})
    

    由于nil 是假的并且在这里永远无效,identity 是一个很好的有效性测试。

    现在我们有了序列,我们可以用它做任何我们喜欢的事情。在你的情况下,我们只是

    • 将地图连接成一个大的地图条目序列;
    • 找到最大key的条目;和
    • 接受它的val

    因此:

    (val (apply max-key key (reduce concat data)))
    ;"ava"
    

    我们应该使用(ensequence (oracle! data) identity) 而不是等价的data,但这没有任何区别。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-13
      • 2015-11-20
      • 1970-01-01
      • 2016-07-14
      相关资源
      最近更新 更多