2024 Clojure 状态调查 中分享您的想法!

欢迎!请参阅 关于 页面了解该功能的更多信息。

0
引用、代理、原子

我知道一点 Lisp,但 Clojure 有点不同

(defn mojorate []
  (let [v (vec (range 1 11))
        max (- (count v) 1)
        atomix (atom [])
        chanl (async/chan)]
    (println v)
    (loop [i 0]
      (async/go
        (>! chanl (swap! atomix conj (/ (get v i) 2))))
      (if (= i max)
        (do (<!! (async/go (<! chanl)))
          (async/close! chanl)
          @atomix)
        (recur (inc i))))))

这只是对通道、原子和 loop/recur 等进行的一个练习。

你能这样做吗?

不太清楚你实际上在问什么 - 你能详细说明一下吗?
它按预期工作,但我不确定它是否 1.:符合规范 2.:最佳实践。3.:或者可以做得更好。
但“预期”是什么意思?函数 *预期* 做什么?在运行它时,我看到了不同的运行结果,因此我无法确切了解你实际需要什么。
by
我总是得到
testomato.core>(mojorate)
[1 2 3 4 5 6 7 8 9 10]                                                                      
[1/2 1 3/2 2 5/2 3 7/2 9/2 5]

这个代码片段向一个通道发送了10个数字,并将它们返回——除以2。

1 答案

+1
by

你有太多的go块,不仅在代码中数量多,而且执行的数量也很多。循环中的第一个go块不会阻塞——它允许立即继续执行。无法保证在相同的块再次执行之前,之前的块有足够的时间将值放入通道。这是一个竞争条件。

为了确认,只需运行以下代码:(set (repeatedly 10000 mojorate))。如果得到的是单个元素的集合,请增加数字。

这个代码片段向一个通道发送了10个数字,并将它们返回——除以2。

句子的前半部分是函数用户角度的一个实现细节,与问题无关。如果您需要的只是从1开始得到10个数字的向量,每个数字除以2,那么当然有(mapv #(/ % 2) (range 1 11))

如果您需要其他内容,请详细描述,以免陷入XY问题。原始函数可以以多种方式重写,但如果您的问题需要特定的东西,则其中任何一个都可能有误。

by
我试图熟悉这个概念。然后我意识到mapv是一个更严谨的解决方案。简单的算术并不罕见来展示这样的事情。以下是一个Common Lisp的例子

(let ((channel (make-channel)))
  (submit-task channel '+ 3 4)
  (receive-result channel))

但我可以证实你的预测:经过10万次重复操作,我得到

#{[1/2 1 3/2 2 5/2 3 7/2 4 9/2 5]
  [1 3/2 2 5/2 3 7/2 4 9/2 5 1/2]
  [1/2 1 3/2 2 5/2 3 7/2 4 9/2]
  [1/2 1 2 5/2 3 7/2 4 9/2 5 3/2]}
by
据我所知,Common Lisp中的channels与`clojure.core.async`中的channels不同。它们更像是与某个线程进行通信的一种方式。在Clojure中,如果你只想在一个单独的线程中运行一系列计算,那么使用`future`或`pmap`会更方便(尽管通常建议避免使用`pmap`,而是使用可控制的选项)。在Clojure中,一个异步channel可能会很有用,如果你想要在一个地方提供值,也许在另一个地方对它们进行一些计算,然后在第三个地方接收结果,而这些地方可能彼此并不知道。
by
当然,Clojure中的channels远远不止这些。它们支持使用不同策略进行缓冲,允许提供背压,让你可以静态或动态地组合和分离channels...
by
谢谢。我现在开始明白了。我之前真的没有看到竞争条件。
...