在Clojure中维护递归函数中的状态

时间:2014-10-01 22:09:03

标签: recursion clojure state sortedmap

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

第一次循环哈希是:

{1 "mary", 2 "dean"}

下一轮吐出

{23 "ava", 4 "scout"}

并且最后一轮返回

 {3 "bina", 16 "bob"}

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

2 个答案:

答案 0 :(得分:1)

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

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

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]

答案 1 :(得分:1)

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

功能

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

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

例如,

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

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

(6 9)

...例如(你的milage 变化)。

为了说明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)

...依次返回其元素,然后是nil s:

(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为false且在此处永不有效,identity是一个很好的有效性测试。

现在我们有了序列,我们可以随心所欲地做任何事情。在你的情况下,我们只是

  • 将地图连接成一系列大的地图条目;
  • 找到最大key的条目;和
  • 取其val

因此:

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

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