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个数字的向量,每个数字除以2,那么当然就是(mapv #(/ % 2) (range 1 11))

如果你需要其他的东西,请详细描述,这样我们就不陷入XY问题。原始函数可以被重写成多种方式——如果您的实际问题需要特定的事情,那么这些都不能保证是正确的。

我试图熟悉这个概念。并且  我意识到mapv是一个更 tight的解决方案。简单的算术并不多见,像这样的事情。

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

但是我确实可以证实你所预测的事情:在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 中的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))] ...))`。
by
当然,Clojure 中的通道不仅仅是这样。它们支持不同的策略进行缓冲,允许进行背压,允许静态或动态地组合和分离通道...
by
reshown by
谢谢。我现在开始清楚地看到这一点。而且我根本没看到竞争条件。
...