drR*_*rtz 8 clojure transducer
有状态转换器预计会在调用“完整”元数(即[([result input] )
)之前根据需要多次调用“步骤”元数(即([result] )
)来刷新其状态。
我的问题是reduced
这里如何处理提前终止(使用 )。
在https://clojure.org/reference/transducers#_early_termination \nit 说
\n\n\n在完成步骤中,具有缩减状态的传感器应在调用嵌套的 Transformer\xe2\x80\x99s 完成函数之前刷新状态,除非它之前已经看到来自嵌套步骤的缩减值,在这种情况下,应丢弃挂起状态。
\n
但“以前见过”是什么意思?
\n对上述引用有三种可能的解释:\n如果嵌套转换器的“步骤”数量已返回一个reduced
值,则“完整”步骤应该
看看partition-by
它的实现,似乎第一个选项适用:
([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
完成步骤中看到一个值,它应该如何反应?
尝试将上面的引用和代码结合起来,我认为它应该停止调用“step”arity并直接调用具有unreduced
类似值的“complete”arity partition-by
。
但是,然后查看代码cat
:
(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
?)
是否只是我碰巧在partition-by
and的实现中发现了两个特殊情况cat
,或者是否存在一些关于在提前终止期间何时调用嵌套“step”函数以及如何传达“reducedness”的一般规则(或者不是)?
据我了解,传感器应该只关心它自己的操作,而不关心它是如何使用或组成的,但这对提前终止有何影响?
\n另外,“步骤”和“完整”参数是否被视为完全独立,前者的提前终止不会影响后者?
\n看看它们的实现,take-while
似乎它们确实是分开的:
(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如果是这样,那么提前终止是否由缩减过程处理,以便在reduced
遇到值时永远不会调用“完成”参数?
更新:
\nRich Hickey 在他的“Inside Transducers”演讲2中对此进行了一些讨论,他说
\n\n\n如果你正在积累,一旦你下面的函数——你正在转换的函数——告诉你它提前终止了,你就不应该再积累了。你应该让自己处于一种状态,这样当你被要求完成时,你会说“我没有任何东西要刷新”,因为你知道下面的功能,但你不想看到它。
\n
这就是我要表达的问题的关键:“停止累积”并不意味着“丢弃所有累积状态”,而是从中查看“步骤”数量,partition-by
只是停止累积并清除其累积状态:
([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)
在完成期间调用,并且遵循“您应该让自己处于一种状态,以便当您被要求完成时说“我没有任何东西要刷新””。
它仍然调用其嵌套变换的“完整”数量 ( (rf result)
)。
reduced
所以:当看到一个值时,传感器应该
reduced
刚刚获得的值,以及(rf result)
?参考演讲:
\n[1] 传感器,https://youtu.be/6mTbuzafcII,30 :40
\n[2] 传感器内部,https://youtu.be/4KqUvG8HPYo,24 :10
\n