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

这只是关于channel、atom loop/recur等的一个练习。

你能这样做吗?

不确定你实际上在问什么 - 请详细说明。
它按预期工作,但我不确定它是否是1.惯用的2.最佳实践3.或者可以做得更好。
但是“预期”是什么意思?*该函数*应该做什么?当运行它时,我在不同的运行中看到不同的结果,所以我不能完全推断出你真正需要什么。
我总是得到
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个数,并返回它们的一半。

1 个回答

+1

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

为了确认,只需运行此代码:(set (repeatedly 10000 mojorate))。如果只得到一个元素的集合,则增加该数字。

代码段向通道发送10个数,并返回它们的一半。

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

如果您还需要其他内容,请详细描述,这样我们就不会陷入XY问题。原始函数可以被重写为多种方式——如果您的实际问题需要特定的东西,则其中任何一个都可能不正确。

我试图了解这一概念。并且我意识到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]}
AFAIK,Common Lisp中的channels与`clojure.core.async`中的channels不同。它们更像是与特定线程进行通信的方式。在Clojure中,如果你只想在另一个线程中运行一系列计算,那么使用`future`或`pmap`会更好(尽管通常建议避免使用`pmap`,而是使用可控的方案)。在Clojure中,异步频道非常有用,如果你想在一个地方输入值,可能在一处对它们做一些计算,并在第三个地方接收结果,而这些地方彼此可能互不知情。例如,一个生产者可能使用`(a/to-chan! (range 1 11))`,一个转换者可能使用`(a/map #(/ % 2) [channel])`,而一个消费者可能使用`(a/go (let [all-values (a/<! (a/into [] transformed-channel))] ...))`。
当然,Clojure中的通道不仅仅是这些。它们支持不同策略的缓冲,允许进行背压,让你可以静态或动态地组合和分离通道...

重新发布
谢谢。我现在终于明白了。我之前真的没有看到竞争条件。
...