Clojure 延迟函数执行

13

好的,这就是我想要做的事情

(defn addresses [person-id]
 ;addresses-retrival )

(defn person [id]
  (merge  {:addresses (addresses id)} {:name "john"}))
在上面的person函数中,我希望地址只在需要时被检索,例如仅当执行以下操作时:
(:addresses (person 10)) 

而不是当

(person 10)

我不确定我是否正确地处理这个问题,因为我是Clojure的新手。

5个回答

11

您可以使用延迟。

(defn person [id]
  (delay  {:addresses (addresses id) :name "john"})) 

(person 2)将返回一个延迟的对象,而不会评估任何内容。使用force或deref(或@)来访问内容并评估延迟对象。

(:addresses @(person 5))

你也可以将延迟仅应用于地址。

(defn person [id]
  {:addresses (delay (addresses id)) :name "john"})

根据你的问题,这可能会更好。

它允许定义:

(defn get-address [person]
  @(:address person))

如何获取延迟计算的地址并强制执行。 (强制执行表示首次计算并在任何其他时间检索强制结果)。


非常感谢。我在想是否可以将其透明化,以便在第一次使用时进行评估,而不是手动强制评估? - Surya
我不这么认为。据我所知,Clojure在惰性和强制方面是明确的。 - Nicolas Oury
我的lazymap库(http://bitbucket.org/kotarak/lazymap)正是如此。它为所有地图类型提供透明的插件,只有在真正检索它们的值时才计算它们的值。 - kotarak

1

至少就序列而言,Clojure非常懒惰,无需告知即可实现。

在这里,将地址检索建模为计数,尝试:

(defn addresses [person-id]
  (iterate #(do (println %) (inc %)) person-id))

(defn person [id]
  (merge  {:addresses (addresses id)} {:name "john"}))

(def people (map person (range 100)))

到目前为止,它还没有打印任何东西,但是如果你这样说:

(doall (take 5 (:addresses (nth people 10))))

那么您应该看到打印发生在需要在第十位中计数五个的情况下。 我想这可能是您想要的行为方式吧?

因此,请让您的地址查找生成一个惰性序列(map、filter、reduce都可以)


1

您可以从addresses函数中返回一个函数,稍后调用该函数将检索地址。就像这样:

(defn addresses [person-id]
 #(;addresses-retrival))

(defn person [id]
  (merge  {:addresses ((addresses id))} {:name "john"}))

请注意,addresses函数返回一个匿名函数(使用#创建),而person函数通过增加一对额外的括号来调用该匿名函数。

0

请记住,延迟是被存储在缓存中的,所以多次调用地址延迟将始终产生与您第一次解引用延迟时相同的地址。

(defn addresses [person-id]
  {:home (str (rand-int 100) " Cool St.") :work "1243 Boring St."})

(defn person [id]
  (merge {:addresses (delay (addresses id))} {:name "john"}))

(let [person1 (person 1)]
  (println @(:addresses person1))
  (println @(:addresses person1)))

这将打印:

{:home 65 Cool St., :work 1243 Boring St.}
{:home 65 Cool St., :work 1243 Boring St.}

注意在延迟的第二次解除引用中,家庭地址没有改变。
如果您不希望出现这种情况,需要使用函数闭包。
(defn addresses [person-id]
  {:home (str (rand-int 100) " Cool St.") :work "1243 Boring St."})

(defn person [id]
  (merge {:addresses (fn [] (addresses id))} {:name "john"}))

(let [person1 (person 1)]
  (println ((:addresses person1)))
  (println ((:addresses person1))))

这将打印:

{:home 16 Cool St., :work 1243 Boring St.}
{:home 31 Cool St., :work 1243 Boring St.}

注意闭包的后续调用中家庭地址已经发生了变化。

因此,如果您的addresses函数具有副作用,比如从数据库中获取地址。而人们可以更改他们的地址,您希望您的代码始终具有最新的地址。这是需要记住的内容,延迟(wait)是否适用于您,或者一个函数闭包是否是更好的选择。


0

我可以建议一些接近您期望的东西。

; Note the use of anonymouns function. #(addresses id)
(defn person [id] 
  (merge  {:addresses #(addresses id)} {:name "john"}))

; :addresses returns a function. Evaluate it by wrapping it in another set of parans.
((:addresses (person 10)))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接