Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/clojure/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Clojure 请帮助我使这个noob代码更加地道_Clojure - Fatal编程技术网

Clojure 请帮助我使这个noob代码更加地道

Clojure 请帮助我使这个noob代码更加地道,clojure,Clojure,因此,我使用congomongo(接近末尾的fetch函数)从mongodb集合中提取一些文档。我想将选项传递给fetch调用,这样我就可以执行类似于(posts:limit 1)的操作,并让{:limit 1}传递给fetch。我正在用@posts进行手动“记忆”,因为我希望能够重置缓存,据我所知,这是用clojure.core/memoize无法完成的 现在,我在这里看到的问题是(fetch:posts options)调用非常重要,如果dosync必须重试事务,我真的不想重击我的数据存储。

因此,我使用congomongo(接近末尾的fetch函数)从mongodb集合中提取一些文档。我想将选项传递给fetch调用,这样我就可以执行类似于
(posts:limit 1)
的操作,并让
{:limit 1}
传递给fetch。我正在用
@posts
进行手动“记忆”,因为我希望能够重置缓存,据我所知,这是用
clojure.core/memoize
无法完成的

现在,我在这里看到的问题是
(fetch:posts options)
调用非常重要,如果dosync必须重试事务,我真的不想重击我的数据存储。我是一个彻头彻尾的clojure/fp noob,但我不知道如何解决这个问题。另外,由于我是一个书呆子,如果我在这里做任何其他让你畏缩的事情,我很想知道如何正确地写这篇文章

(def posts (ref nil))
(defn reset-posts [] (dosync alter posts nil))

(defn fetch-posts [& options]
  (let [options (apply array-map options)]
    (or @posts
        (dosync alter posts (fetch :posts options)))))

我不相信您的事务块(
(dosync alter
…)会按照您的想法执行

user=> (def posts (ref nil))
#'user/posts
user=> (dosync (ref-set posts [1 2 3 4 5]))
[1 2 3 4 5]
user=> @posts
[1 2 3 4 5]
user=> (dosync alter posts nil)
nil
user=> @posts
[1 2 3 4 5]
重置帖子
中,您可能需要
(dosync(ref set posts nil))
,而在
获取帖子
中,语法修复将是
(dosync(ref set posts(fetch:posts选项))

但是,在
获取帖子
中有一个竞态条件,一个检查然后执行。可能没什么大不了的;不确定谁使用了
获取帖子
,但是在事务中移动
或@posts
位可以避免两个并发事务最终都提交alter的情况


关于重试
获取帖子
,是的,这是可能发生的,尽管您的缓存解决方案避免了大多数。但是,我不确定是否有一种不锁定的方法。通常,对于事务中的I/O内容,您会将其转包给代理,但事务的成功取决于
获取
的返回值,因此它是有效的我不清楚这是怎么回事。

所以你之所以引入ref,是因为你不想在时间流逝时破坏内存,因为在回帖周围使用memoize可能迟早会导致这种情况,对吧

也许你可以尝试另一种方法:让fetchposts“纯粹”,不需要记忆。在这种情况下,有人可以盲目地调用fetchposts,而不必担心内存异常。 事实上,对于某些用例,在调用代码的本地代码中“缓存值”就足够了

但是故事并没有到此结束,否则我就不会花时间回答:-):通过使用clojure.core/binding重新绑定fetch posts,您可以很容易地实现“时间本地化”的回忆录:从那时起,调用堆栈中同一线程中的所有代码都将受益于绑定的回忆录posts。 如果您使用的是clojure 1.3 alpha,则需要通过:dynamic metadata将fetch posts变量声明为可显式重新绑定

;; most simple definition
(defn ^:dynamic fetch-posts [& options]
  (let [options (apply array-map options)]
    (fetch :posts options)))

;; a la carte caching by the calling code (lexically scoped)
(let [posts (apply fetch-posts options)] ...)

;; a la carte caching by the calling code (dynamically scoped)
(binding [fetch-posts (memoize fetch-posts)] ...)
我的最后一个猜测是,你会想在文章中“记忆”,在你的原始版本中,通过一个键对文章进行索引,这将是选项seq,对吗?也许你的代码不对?(或者您假设fetch post总是使用相同的参数反复调用?)

另一个想法。使用代理序列化对POST的写访问权限,然后确保仅当fetch为nil时才执行fetch调用:

    (def posts (agent nil))

    (defn reset-posts [] (send posts (constantly nil)))

    (defn fetch-posts [& options]
      (let [options (apply array-map options)]
        (send-off posts #(or % (fetch :posts options)))
        (await-for (Long/MAX_VALUE) posts)
        @posts))

另一种可能有助于将大量计算移到
dosync
之外的方法是使用
delay

(defn fetch-posts
  [& options]
  @(dosync (or @posts (ref-set posts (delay (apply fetch :posts options))))))
还要注意,原始代码不是线程安全的,因为您在
dosync
外部访问ref,然后在
dosync
中基于此值对其进行修改。但是该值可能已经在
deref
dosync
之间更改。例如,另一个线程并行调用
fetchposts

代理方法也是有问题的,因为您无法可靠地读取代理。您获得的值是一致的,但访问不同步。考虑劳伦特的例子:在<代码>等待< /COD>和<代码> DREF < /代码>另一个线程可能已经调用<代码>重置帖子,而不是<邮政编码> NIL<代码>。在这个例子中,这可能是一个“牵强附会和B”,也许是一个必须考虑的情况,但是可能会有其他用例在更关键的代码中引入一个微妙的竞争条件。
tl;医生:小心你做的事!Clojure不是神奇的线程安全。仔细思考您的解决方案并了解其含义。

尝试询问,谢谢您的建议:)阅读了一些内容,似乎ref set和alter之间的区别在于alter运行一个函数来设置值,ref set只是将其设置为您传递的值,所以是的,nil作为参数没有任何意义。当然。另一个重要的区别是,
dosync
采用表单进行求值,因此在最初的示例中,
alter
实际上从未被调用-只是作为函数求值,就像
posts
nil
一样。类似于
(dosync(alter posts assoc:foo“bar”)
的东西,如果
@posts
一开始是一个地图的话,它会起作用。是的,当它处于“某种工作”状态时,我发布了这个问题,我意识到我必须快速缓存更多的结果。您给出的第一个示例可能是我将采用的方法,但感谢代理示例,我对clojure并发模型了解得越多,我就越喜欢它。这是真的,在代理的fetch post中有一个竞争条件:-(