clojure参考文献contains以下关于传感器的评论,看起来像是在写一些关于写作和使用传感器的安全性的重要事项:
如果您有应用传感器的新环境,则需要注意以下几条一般规则:
如果步进函数返回减小的值,则转换过程不得再向步进函数提供任何输入。该 减值必须在完成前用deref打开。
完成过程必须完成一次最终累计值的完成操作。
- 转换过程必须封装对通过调用换能器返回的函数的引用 - 这些可能是有状态且不安全的 用于跨线程。
您是否可以通过一些示例解释每个案例的含义?另外," context"在这方面指的是什么?
谢谢!
答案 0 :(得分:4)
此场景的一个示例是take-while
传感器:
(fn [rf]
(fn
([] (rf))
([result] (rf result))
([result input]
(if (pred input)
(rf result input)
(reduced result)))))
正如你所看到的,它可以返回一个reduced
值,这意味着为这样的步进函数提供更多输入没有意义(实际上这将是一个错误) - 我们知道已经没有了产生的价值。
例如,在我们达到值(1 1 3 5 6 8 7)
时,使用odd?
谓词处理6
输入集合时,take-while odd?
传感器创建的步进函数不会再返回任何值。
这是换能器返回有状态步进功能的情况。一个很好的例子是partition-by
换能器。例如,当转化过程使用(partition-by odd?)
处理(1 3 2 4 5 2)
时,它将生成((1 3) (2 4) (5) (6 8))
。
(fn [rf]
(let [a (java.util.ArrayList.)
pv (volatile! ::none)]
(fn
([] (rf))
([result]
(let [result (if (.isEmpty a)
result
(let [v (vec (.toArray a))]
;;clear first!
(.clear a)
(unreduced (rf result v))))]
(rf result)))
([result input]
(let [pval @pv
val (f input)]
(vreset! pv val)
(if (or (identical? pval ::none)
(= val pval))
(do
(.add a input)
result)
(let [v (vec (.toArray a))]
(.clear a)
(let [ret (rf result v)]
(when-not (reduced? ret)
(.add a input))
ret))))))))
如果您查看实现,您会注意到step函数不会返回它的累积值(存储在a
数组列表中),直到谓词函数返回不同的值结果(例如,在一系列奇数之后,它将接收一个偶数,它将返回一个累积奇数的seq)。问题是如果我们到达源数据的末尾 - 将没有机会观察到谓词结果值的变化,并且不会返回累积值。因此,转换过程必须调用步骤函数(arity 1)的完成操作,以便它可以返回其累积结果(在我们的例子中为(6 8)
)。
当通过传递源数据和传感器实例执行转换过程时,它将首先调用传感器函数以产生步进函数。 The transducer is a function of the following shape:
(fn [xf]
(fn ([] ...)
([result] ...)
([result input] ...)))
因此,转换过程将调用此顶级函数(接受xf
- 缩减函数)以获得用于处理数据元素的实际步骤函数。问题是转换过程必须保持对该步骤函数的引用,并使用相同的实例来处理来自特定数据源的元素(例如,必须使用生成的步骤函数实例partition-by
转换器来处理整个输入序列因为它保持其内部状态,如上所示)。使用不同的实例处理单个数据源会产生不正确的结果。
类似地,由于相同的原因,转换过程不能重用步骤函数实例来处理多个数据源 - 步骤函数实例可能是有状态的并且保持处理特定数据源的内部状态。当step函数用于处理另一个数据源时,该状态将被破坏。
此外,无法保证步骤函数实现是否是线程安全的。
"应用传感器的新背景"意味着实施一种新型的转导过程。 Clojure提供了与集合一起使用的可转换过程(例如into
,sequence
)。 core.async库chan
函数(其中一个arities)接受一个转换器实例作为参数,它通过将转换器应用于消耗的值来产生异步转换过程,从而产生值(可以从通道中消耗)。 p>
例如,您可以创建一个可转换的流程来处理在套接字上接收的数据,或者您自己的可观察实现。
当数据来自(套接字,流,集合,事件源等)时,它们可以使用传感器来转换数据,因为传感器是不可知的 - 它只是一个用单个元素调用的函数。 / p>
他们也不关心(并且不知道)应该对他们生成的结果做些什么(例如,是否应该将其附加到结果序列中(例如conj
)?它是通过网络发送的?插入数据库?) - 它是通过使用由步骤函数(上面的rf
参数)捕获的reduce函数抽象的。
因此,我们不是创建仅使用conj
或将元素保存到db的步骤函数,而是传递一个具有该操作的特定实现的函数。并且您的可转换流程定义了该操作的内容。