clojure延迟函数执行

clojure延迟函数执行,clojure,lazy-evaluation,Clojure,Lazy Evaluation,好的,这就是我要做的 (defn addresses [person-id] ;addresses-retrival ) (defn person [id] (merge {:addresses (addresses id)} {:name "john"})) 在上面的person函数中,我希望只在需要时检索地址,就像只在需要时检索一样 (:addresses (person 10)) 而不是什么时候 (person 10) 作为clojure的新手,我不确定我是否正确地处理了

好的,这就是我要做的

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

(defn person [id]
  (merge  {:addresses (addresses id)} {:name "john"}))
在上面的person函数中,我希望只在需要时检索地址,就像只在需要时检索一样

(:addresses (person 10)) 
而不是什么时候

(person 10)
作为clojure的新手,我不确定我是否正确地处理了这个问题。

您可以使用延迟

(defn person [id]
  (delay  {:addresses (addresses id) :name "john"})) 
(人员2)随后将返回延迟的,不进行任何评估的报告。 要访问内容并评估延迟对象,请使用force或deref(或@)

或者,您可以只在地址上设置延迟

(defn person [id]
  {:addresses (delay (addresses id)) :name "john"})
根据你的问题,哪一个更好

它允许定义:

(defn get-address [person]
  @(:address person))
这将获取延迟地址并强制执行。
(强制意味着第一次计算并在任何其他时间检索强制结果)。

至少就序列而言,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))))
然后,你会看到印刷发生在需要发生的情况下,在第十位的五个。我想那可能是你想要的那种行为


因此,让您的地址查找生成一个惰性序列(映射、筛选、减少都可以)

我可以建议一些接近您期望的内容

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

您可以从
地址
函数返回一个函数,稍后调用该函数时将检索地址。大概是这样的:

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

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

请注意,
addresses
函数返回一个匿名函数(使用
#
创建),而
person
函数使用一对额外的参数调用该匿名函数。

请记住,延迟会被记忆,因此,地址延迟的连续调用将始终产生与第一次取消延迟时相同的地址

(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.}
{:home 16 Cool St., :work 1243 Boring St.}
{:home 31 Cool St., :work 1243 Boring St.}
请注意家庭地址在延迟的第二个deref中是如何保持不变的

如果不希望出现这种行为,则需要使用函数闭包

(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 65 Cool St., :work 1243 Boring St.}
{:home 65 Cool St., :work 1243 Boring St.}
{:home 16 Cool St., :work 1243 Boring St.}
{:home 31 Cool St., :work 1243 Boring St.}
请注意,在随后的闭包调用中,家庭地址是如何不同的


所以,如果您是
地址
函数会产生副作用,比如从数据库获取地址。这些人可以更改他们的地址,您希望您的代码始终具有最新的地址,如果延迟对您有效,或者函数关闭是更好的选择,请记住这一点。

非常感谢。我想知道这是否可以透明化,以便在首次使用时对其进行评估。而不是手动强制评估?我不这么认为。据我所知,Clojure在懒惰和暴力方面表现得很明显。我的lazymap就是这样做的。它为所有贴图类型提供透明的下拉列表,只有在真正检索到它们时,才会计算它们的值。