Clojure 序列与传感器有什么关系?

Clojure 序列与传感器有什么关系?,clojure,transducer,Clojure,Transducer,关于序列的两个相关问题: 给定一个传感器,例如(def xf(comp(filter odd?)(map inc)) (进入[]xf(范围10))或(进入()xf(范围10))和(序列xf(范围10))之间有什么关系?只是因为没有惰性序列的语法可以用作到的第二个参数,所以我们需要一个单独的函数序列?(我知道,序列还有另一种非传感器用途,将一个集合强制转换成一种或另一种序列。) 上面提到的关于序列的用法 生成的序列元素以增量方式计算。这些序列将根据需要递增地消耗输入,并完全实现中间操作。此行为不同

关于序列的两个相关问题:

给定一个传感器,例如
(def xf(comp(filter odd?)(map inc))

  • (进入[]xf(范围10))
    (进入()xf(范围10))
    (序列xf(范围10))
    之间有什么关系?只是因为没有惰性序列的语法可以用作
    的第二个参数,所以我们需要一个单独的函数
    序列
    ?(我知道,
    序列
    还有另一种非传感器用途,将一个集合强制转换成一种或另一种序列。)

  • 上面提到的关于
    序列的用法

  • 生成的序列元素以增量方式计算。这些序列将根据需要递增地消耗输入,并完全实现中间操作。此行为不同于惰性序列上的等效操作

    对我来说,听起来好像
    sequence
    没有返回延迟序列,但是
    sequence
    的docstring表示“当提供传感器时,将转换的延迟序列返回到coll(s)中的项,…”,事实上
    (类(序列xf(范围10)))
    返回
    clojure.lang.LazySeq
    。我想我不明白上面Clojure Transformers页面中引用的最后一句话。

    (序列xform-from)
    在传递xform和from的Transformer上创建惰性seq(RT.chunkIteratorSeq)。当请求下一个值时,将对来自的下一个值调用xform(转换的组合)

    此行为不同于惰性序列上的等效操作

    惰性序列上的等价操作是什么?以您的xf为例, 将
    过滤奇数?
    应用于
    (范围10)
    ,生成中间惰性序列,并将
    映射inc
    应用于中间惰性序列,生成最终惰性序列作为结果

    我想说,当from是一个不实现IReduceInit的集合时,
    (into-to-xform-from)
    类似于
    (into-to(sequence-xform-from))

    into内部使用
    (将xform conj转换为from)
    执行以下操作: 与
    (reduce(xform conj)to from)相同
    ,最后称为clojure.core.protocols/coll-reduce:

    (into [] (sequence xf (range 10)))
    ;[2 4 6 8 10]
    (into [] xf (range 10))
    ;[2 4 6 8 10]
    (transduce xf conj [] (range 10))
    ;[2 4 6 8 10]
    (reduce (xf conj) [] (range 10))
    ;[2 4 6 8 10]
    
    我将您的传感器修改为:

    (defn hof-pr
         "Prints char c on each invocation of function f within higher order function"
     ([hof f c]
       (hof (fn [e] (print c) (f e))))
     ([hof f c coll]
       (hof (fn [e] (print c) (f e)) coll)))
    (def map-inc-pr (partial hof-pr map inc \m))
    (def filter-odd-pr (partial hof-pr filter odd? \f))
    (def xf (comp (filter-odd-pr) (map-inc-pr)))
    
    这样它就可以在每个转换步骤上打印出字符

    在REPL中创建s1,如下所示:

    (def s1 (into [] xf (range 10)))
    ffmffmffmffmffm
    
    s1被急切地评估(打印f用于过滤,打印m用于映射)。再次请求s1时无评估:

    s1
    [2 4 6 8 10]
    
    让我们创建s2:

    仅对s2中的第一项进行评估。下一个项目将根据要求进行评估:

    s2
    ffmffmffmffm(2 4 6 8 10)
    
    此外,创建s3,旧方法:

    (def s3 (map-inc-pr (filter-odd-pr (range 10))))
    s3
    ffffffffffmmmmm(2 4 6 8 10)
    

    如您所见,定义s3时没有计算。当请求s3时,将应用筛选10个元素,然后应用其余5个元素的映射,生成最终序列。

    我发现当前的答案不够清楚,因此

    sequence
    确实返回一个LazySeq,但它是一个分块的,因此当您在REPL中使用它时,您通常会觉得它很急切,因为您的集合可能太小,分块会使它看起来很急切。我认为块的大小是有点动态的,它并不总是完全相同大小的块,但总的来说,它的大小似乎是32。因此,您的传感器将一次应用于输入采集32个元素,延迟

    这是一个简单的传感器,它只需打印减少的元素,并将其原封不动地返回:

    (defn printer
      [xf]
      (fn
        ([] (xf))
        ([result] (xf result))
        ([result input]
         (println input)
         (xf result input))))
    
    如果我们用它创建一个包含100个元素的序列
    s

    (def s
      (sequence
       printer
       (range 100)))
    ;;> 0
    
    我们看到它打印
    0
    ,但没有其他内容。调用
    sequence
    时,第一个元素将从
    (范围100)
    消耗,并将其传递给
    xf
    链进行转换,在本例中,该链只打印它。除第一种元素外,没有其他元素被消耗

    现在,如果我们从
    s
    中提取一个元素:

    (take 1 s)
    ;;> 0
    ;;> 1
    ;;> 2
    ;;> 3
    ;;> 4
    ;;> 5
    ;;> 6
    ;;> 7
    ;;> 8
    ;;> 9
    ;;> 10
    ;;> 11
    ;;> 12
    ;;> 13
    ;;> 14
    ;;> 15
    ;;> 16
    ;;> 17
    ;;> 18
    ;;> 19
    ;;> 20
    ;;> 21
    ;;> 22
    ;;> 23
    ;;> 24
    ;;> 25
    ;;> 26
    ;;> 27
    ;;> 28
    ;;> 29
    ;;> 30
    ;;> 31
    ;;> 32
    
    我们看到它打印了前32个元素。这是Clojure中分块惰性序列的正常行为。您可以认为它是半懒惰的,因为它一次消耗块大小的元素,而不是一次消耗1个

    现在,如果我们尝试获取1到32之间的任何元素,则不会打印任何其他元素,因为前32个元素已被处理:

    (take 1 s)
    ;; => (0)
    (take 10 s)
    ;; => (0 1 2 3 4 5 6 7 8 9)
    (take 24 s)
    ;; => (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23)
    (take 32 s)
    ;; => (0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31)
    
    不打印任何内容,每次获取都返回预期的结果集。我正在使用
    ;=>
    ;;>用于打印输出

    好的,现在如果我们使用第33个元素,我们希望看到下一块32个元素被打印出来:

    (take 33 s)
    ;;> 33
    ;;> 34
    ;;> 35
    ;;> 36
    ;;> 37
    ;;> 38
    ;;> 39
    ;;> 40
    ;;> 41
    ;;> 42
    ;;> 43
    ;;> 44
    ;;> 45
    ;;> 46
    ;;> 47
    ;;> 48
    ;;> 49
    ;;> 50
    ;;> 51
    ;;> 52
    ;;> 53
    ;;> 54
    ;;> 55
    ;;> 56
    ;;> 57
    ;;> 58
    ;;> 59
    ;;> 60
    ;;> 61
    ;;> 62
    ;;> 63
    ;;> 64
    
    太棒了!因此,我们再次看到,只取了接下来的32个元素,这使我们现在总共处理了64个元素

    这表明,用转换器调用的
    sequence
    实际上创建了一个延迟的分块序列,在该序列中,只有在需要时才会处理元素(一次的分块大小)

    这是怎么回事

    生成的序列元素以增量方式计算。这些序列将根据需要递增地消耗输入,并完全实现中间操作。此行为不同于惰性序列上的等效操作

    这是关于操作发生的顺序。带
    序列
    和传感器:

    (sequence (comp A B C) coll)
    
    对于区块中的每个元素,是否会让它们通过:
    A->B->C
    ,因此您得到:

    A(e1) -> B(e1) -> C(e1)
    A(e2) -> B(e2) -> C(e2)
    ...
    A(e32) -> B(e32) -> C(e32)
    
    而对于普通的惰性序列,如:

    (->> coll A B C)
    
    将首先让所有分块元素通过A,然后让它们都通过B,然后是C:

    A(e1)
    A(e2)
    ...
    A(e32)
    |
    B(e1)
    B(e2)
    ...
    B(e32)
    |
    C(e1)
    C(e2)
    ...
    C(e32)
    
    这需要在每个步骤之间进行中间收集,因为A的结果必须收集到一个集合中,然后循环并应用B,等等

    我们
    A(e1)
    A(e2)
    ...
    A(e32)
    |
    B(e1)
    B(e2)
    ...
    B(e32)
    |
    C(e1)
    C(e2)
    ...
    C(e32)
    
    (def s
      (sequence
       (comp (filter odd?)
             printer
             (map vector)
             printer)
       (range 10)))
    
    (take 1 s)
    ;;> 1
    ;;> [1]
    ;;> 3
    ;;> [3]
    ;;> 5
    ;;> [5]
    ;;> 7
    ;;> [7]
    ;;> 9
    ;;> [9]
    
    
    (def l
      (->> (range 10)
           (filter odd?)
           (map #(do (println %) %))
           (map vector)
           (map #(do (println %) %))))
    
    (take 1 l)
    ;;> 1
    ;;> 3
    ;;> 5
    ;;> 7
    ;;> 9
    ;;> [1]
    ;;> [3]
    ;;> [5]
    ;;> [7]
    ;;> [9]
    
    (def s
      (sequence
       (comp printer
             (filter odd?))
       (range 100)))
    
    (take 1 s)
    ;;> 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
    
    (def l
      (->> (range 100)
           (map #(do (print % "") %))
           (filter odd?)))
    
    (take 1 l)
    ;;> 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31