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.:或者可以做得更好。
但是“预期”是什么意思?这个函数本应做什么?运行它时,我看到了每次运行都产生不同的结果,因此我无法确切地推断出你真正需要什么。
我总是得到
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

你有太多的go块,不仅是指在代码中它们的数量,而且还包括执行的数量。循环中的第一个go块不会阻塞 - 它允许立即继续执行。不能保证在该块再次执行之前,前面的块有足够的时间将值放入通道中。这是一个竞争条件。

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

片段发送10个数字到一个通道中,并返回它们(除以2)。

句子的前一半是对用户来说无关紧要的实现细节。如果你需要的只是从1开始分成2的部分的10个数的向量,那么当然是(mapv #(/ % 2) (range 1 11))

如果你需要其他东西,请详细描述,这样我们就不会陷入XY问题。原始函数可以以多种方式重写,但如果你的实际问题需要特定的东西,那么它们中没有一个可能是正确的。

我试图熟悉这个概念。我意识到mapv是更紧密的解决方案。简单的算术在展示这类事物时并不少见。Common Lisp中的示例

(let ((channel (make-channel)))
  ;(提交任务通道 '+ 3 4)
  ;(接收结果通道)

但我可以确认你预测的情况:经过100,000次重复,我得到

#{[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中的通道与`clojure.core.async`中的通道不同。它们更像是一种与特定线程的通信方式。在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))] ...))`。
by
当然,Clojure中的通道远不止这些。它们支持使用不同策略进行缓冲,允许进行背压,可以静态或动态地组合和分离通道……
by
reshown by
谢谢。我现在开始明白了。我真的没有看到竞争条件。
...