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

欢迎!有关这个网站如何工作的更多信息,请参阅关于页面。

+3票数
度量转换器
重新分类

我正在尝试实现一个简单的状态度量转换器,以统计项目的数量(使用ClojureScript)

(defn stateful-counter []
  (fn [xf]
    (let [counter (atom 0)]

      (fn
        ([] (xf))

        ([result]
         (xf (xf result @counter)))

        ([result _]
          (swap! counter inc)
          result)))))

当在一个序列上运行它时,我得到以下输出

(into [] (stateful-counter) (range 5))

[5]

这正是我预期的。

当我在核心.async通道上运行它时,我得到了一个由5组成的无限序列

(go (println
     (<! (let [c (async/chan 1 (stateful-counter))]

           (async/onto-chan! c (range 5))

           (async/into []
                       (async/take 10 c))))))

[5 5 5 5 5 5 5 5 5 5]

如果我不使用 `(async/take 10 _)`,似乎会陷入无限循环。期望的结果是 [5]。

我还尝试使用来自xforms库的`net.cgrand.xforms/count`并得到相同的不期望的结果

(go (println
     (<! (let [c (async/chan 1 xforms/count)]

           (async/onto-chan! c (range 5))

           (async/into []
                       (async/take 10 c))))))

我假设我在`stateful-counter`中做了实现错误,导致了循环。但是,我很困惑,xforms/count产生了相同(不期望)的结果。

这是一个ClojureScript片段,但我已经能够使用Clojure生成相同的不期望结果。

有人能帮我理解为什么在核心.async通道上应用状态度量转换器会导致无限序列吗?

EDIT:在Clojure中看到了相同的不期望行为。

1 答案

+1

查看 partition-by
https://github.com/clojure/clojure/blob/clojure-1.10.1/src/clj/clojure/core.clj#L7160
特别是注释“;;clear first!”,这是Clojure核心中的两个带感叹号的注释之一。

谢谢!添加“(reset! counter nil)”(类似于“(.clear a)”在partition-by中)可以使它工作

    (defn stateful-counter []
      (fn [xf]
        (let [counter (atom 0)]
    
          (fn
            ([] (xf))
    
               ([result]
               (let [c @counter]
               (reset! counter nil)
               (xf (xf result c))))
    
            ([result _]
               (swap! counter inc)
             result)))))

我仍然不明白为什么。你能给我一个提示发生了什么吗?

这也似乎应该是xforms库中的bug?
嗯,手册(《https://clojure.org/reference/transducers》)说,“完成(arity 1)... 这个arity必须正好调用一次rf完成arity。” 可以更广泛或更强调,比如“永远只调用一次。” 无论如何,你现在程序符合这个规则,也能正常工作。P.S. 根据我所知,可以通过GitHub上的网站项目提交一个文档增强建议,项目链接为《https://github.com/clojure/clojure-site
图像
这个调用不会正好只调用一次rf完成arity吗?

```
 (xf (xf result @counter))
```
图像
编辑
我认为这个要求是传递性的:你应该调用一次完成arity,但你自己的完成arity也必须只调用一次。
对我来说,`partition-all`清除更多地关乎内存问题(不要过度引用可能可以回收的对象),而不是转换器。
(已编辑以纠正自动校正)
...