Transducers:如果嵌套步骤数量返回减少,那么完成数量应该做什么?

drR*_*rtz 8 clojure transducer

有状态转换器预计会在调用“完整”元数(即[([result input] ))之前根据需要多次调用“步骤”元数(即([result] ))来刷新其状态。

\n

我的问题是reduced这里如何处理提前终止(使用 )。

\n

https://clojure.org/reference/transducers#_early_termination \nit 说

\n
\n

在完成步骤中,具有缩减状态的传感器应在调用嵌套的 Transformer\xe2\x80\x99s 完成函数之前刷新状态,除非它之前已经看到来自嵌套步骤的缩减值,在这种情况下,应丢弃挂起状态。

\n
\n

但“以前见过”是什么意思?

\n

对上述引用有三种可能的解释:\n如果嵌套转换器的“步骤”数量已返回一个reduced值,则“完整”步骤应该

\n
    \n
  1. 仍然通过调用嵌套的“step”数量来刷新状态,
  2. \n
  3. 忽略它的状态并只调用“完整”元数,或者
  4. \n
  5. 根本不调用嵌套变压器的任何数量
  6. \n
\n

看看partition-by它的实现,似乎第一个选项适用:

\n
   ([result]\n           (let [result (if (.isEmpty a)\n                          result\n                          (let [v (vec (.toArray a))]\n                            ;;clear first!\n                            (.clear a)\n                            (unreduced (rf result v))))]\n             (rf result)))\n
Run Code Online (Sandbox Code Playgroud)\n

在将此未减少的值传递给嵌套变换的“完整”数量之前,这还会从使用剩余状态调用嵌套变换的“步骤”数量的结果中删除“减少”。

\n

那么问题是,如果我有一个有状态的减速器,在其完成步骤中会多次调用其嵌套转换的“步骤”数量,那么如果它在reduced完成步骤中看到一个值,它应该如何反应?

\n

尝试将上面的引用和代码结合起来,我认为它应该停止调用“step”arity并直接调用具有unreduced类似值的“complete”arity partition-by

\n

但是,然后查看代码cat

\n
(defn ^:private preserving-reduced\n  [rf]\n  #(let [ret (rf %1 %2)]\n     (if (reduced? ret)\n       (reduced ret)\n       ret)))\n\n(defn cat\n  "A transducer which concatenates the contents of each input, which must be a\n  collection, into the reduction."\n  {:added "1.7"}\n  [rf]\n  (let [rrf (preserving-reduced rf)]  \n    (fn\n      ([] (rf))\n      ([result] (rf result))\n      ([result input]\n         (reduce rrf result input)))))\n
Run Code Online (Sandbox Code Playgroud)\n

它将任何减少的结果包装在“步骤”数量中的另一个减少“层”中。(稍后被删除reduce?)

\n

是否只是我碰巧在partition-byand的实现中发现了两个特殊情况cat,或者是否存在一些关于在提前终止期间何时调用嵌套“step”函数以及如何传达“reducedness”的一般规则(或者不是)?

\n

据我了解,传感器应该只关心它自己的操作,而不关心它是如何使用或组成的,但这对提前终止有何影响?

\n

另外,“步骤”和“完整”参数是否被视为完全独立,前者的提前终止不会影响后者?

\n

看看它们的实现,take-while似乎它们确实是分开的:

\n
(fn\n         ([] (rf))\n         ([result] (rf result))\n         ([result input]\n            (if (pred input)\n              (rf result input)\n              (reduced result))))))\n
Run Code Online (Sandbox Code Playgroud)\n

在 StrangeLoop 演讲1的行李处理程序示例中,完成数量(taking-while non-ticking?)仍会将先前步骤中的任何剩余行李放到飞机上,无论它返回的“步骤”数量是否减少。

\n

这是正确的解释吗?\n如果是这样,那么提前终止是否由缩减过程处理,以便在reduced遇到值时永远不会调用“完成”参数?

\n

更新:

\n

Rich Hickey 在他的“Inside Transducers”演讲2中对此进行了一些讨论,他说

\n
\n

如果你正在积累,一旦你下面的函数——你正在转换的函数——告诉你它提前终止了,你就不应该再积累了。你应该让自己处于一种状态,这样当你被要求完成时,你会说“我没有任何东西要刷新”,因为你知道下面的功能,但你不想看到它。

\n
\n

这就是我要表达的问题的关键:“停止累积”并不意味着“丢弃所有累积状态”,而是从中查看“步骤”数量,partition-by只是停止累积并清除其累积状态:

\n
([result input]\n           (let [pval @pv\n                 val (f input)]\n             (vreset! pv val)\n             (if (or (identical? pval ::none)\n                     (= val pval))\n               (do\n                 (.add a input)\n                 result)\n               (let [v (vec (.toArray a))]\n                 (.clear a)\n                 (let [ret (rf result v)]\n                   (when-not (reduced? ret)\n                     (.add a input))\n                   ret)))))))))\n
Run Code Online (Sandbox Code Playgroud)\n

因此它不会(rf result v)在完成期间调用,并且遵循“您应该让自己处于一种状态,以便当您被要求完成时说“我没有任何东西要刷新””。

\n

它仍然调用其嵌套变换的“完整”数量 ( (rf result))。

\n

reduced所以:当看到一个值时,传感器应该

\n
    \n
  • 在“step”参数中,清除其累加状态并返回reduced刚刚获得的值,以及
  • \n
  • 在“完整”数量中,只需传递它获得的任何值而不进行任何转换(rf result)
  • \n
\n

参考演讲:

\n

[1] 传感器,https://youtu.be/6mTbuzafcII,30 :40

\n

[2] 传感器内部,https://youtu.be/4KqUvG8HPYo,24 :10

\n