了解clojure传感器的缺陷

时间:2017-05-26 17:37:56

标签: clojure

clojure参考文献contains以下关于传感器的评论,看起来像是在写一些关于写作和使用传感器的安全性的重要事项:

  

如果您有应用传感器的新环境,则需要注意以下几条一般规则:

     
      
  • 如果步进函数返回减小的值,则转换过程不得再向步进函数提供任何输入。该   减值必须在完成前用deref打开。

  •   
  • 完成过程必须完成一次最终累计值的完成操作。

  •   
  • 转换过程必须封装对通过调用换能器返回的函数的引用 - 这些可能是有状态且不安全的   用于跨线程。
  •   

您是否可以通过一些示例解释每个案例的含义?另外," context"在这方面指的是什么?

谢谢!

1 个答案:

答案 0 :(得分:4)

如果步进函数返回减小的值,则转换过程不得再向步进函数提供任何输入。减少的值必须在完成前用deref解包。

此场景的一个示例是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函数用于处理另一个数据源时,该状态将被破坏。

此外,无法保证步骤函数实现是否是线程安全的。

" context"在这方面指的是什么?

"应用传感器的新背景"意味着实施一种新型的转导过程。 Clojure提供了与集合一起使用的可转换过程(例如intosequence)。 core.async库chan函数(其中一个arities)接受一个转换器实例作为参数,它通过将转换器应用于消耗的值来产生异步转换过程,从而产生值(可以从通道中消耗)。 p>

例如,您可以创建一个可转换的流程来处理在套接字上接收的数据,或者您自己的可观察实现。

当数据来自(套接字,流,集合,事件源等)时,它们可以使用传感器来转换数据,因为传感器是不可知的 - 它只是一个用单个元素调用的函数。 / p>

他们也不关心(并且不知道)应该对他们生成的结果做些什么(例如,是否应该将其附加到结果序列中(例如conj)?它是通过网络发送的?插入数据库?) - 它是通过使用由步骤函数(上面的rf参数)捕获的reduce函数抽象的。

因此,我们不是创建仅使用conj或将元素保存到db的步骤函数,而是传递一个具有该操作的特定实现的函数。并且您的可转换流程定义了该操作的内容。