2024年Clojure状态调查!中分享你的想法。

欢迎!请查看关于页面以获取更多关于如何使用本站的信息。

+2
序列

我意识到当发生拆分时,partition-by中的f对于同一元素调用两次

(partition-by
 #(do (println %)
   (rand))
 [1 2 3])

1
2
2
3
3
((1) (2) (3))

看看每次拆分时,元素2和3是如何被打印两次的。

这是bug吗?它应该这样做吗?

我注意到因为我有一个需要计数元素出现次数以知道何时拆分的场景,这弄乱了我的计数。

编辑:看起来转换器版本的版本没有这种行为

(into []
 (partition-by
  #(do (println %)
    (rand)))
 [1 2 3])

1
2
3
[[1] [2] [3]]

1答案

+1

编辑
([f coll]
 (lazy-seq
  (when-let [s (seq coll)]
    (let [fst (first s)
          fv (f fst)
          run (cons fst (take-while #(= fv (f %)) (next s)))]
      (cons run (partition-by f (lazy-seq (drop (count run) s))))))))

看起来f在绑定fv时被调用1次。由于take-while部分不会产生任何类似的键,所以run的值是'(1)。然而,f "曾经"在s的其余部分的第一值上被调用,因此2被打印1次。当在懒序列构建中,partition-by伪递归时,新分区的第一个参数现在是2。《run》的值是通过在2上调用f确定的,所以打印出两个,等等。这个过程重复。

传感器变体绝不会递归地通过下一个分区的头部,因此它仅在每个原始输入元素上调用一次 f

注意:我们可以这样得出一个不需要状态的1次分区函数变体

user> (defn partition-by-once [f coll]
        (->> coll
             (map (fn [x] [(f x) x]))
             (partition-by first)
             (map #(map second %))))
#'user/partition-by-once
user> (partition-by-once #(do (println %)
                               (rand)) [1 2 3])
1
2
3
((1) (2) (3))

我们也可以实现传感器使用的易失性技巧

    (defn partition-by-once-statefully
      [f coll]
      (let [pv   (volatile! ::none)
            test (fn [v] (vreset! pv (f v)))
            aux  (fn aux
                   ([coll]
                    (lazy-seq
                     (when-let [s (seq coll)]
                       (let [fst (first coll)
                             fv  @pv
                             run (cons fst (take-while #(= fv (test %)) (next s)))]
                         (cons run (aux (lazy-seq (drop (count run) s))))))))
                  ([fst remaining]
                   (if-let [s (seq remaining)]
                     (let [fv  (test fst)
                           run (cons fst (take-while #(= fv (test %)) s))]
                       (cons run (aux (lazy-seq (drop (dec (count run)) s)))))
                     `((~fst)))))]
        (when-let [s (seq coll)]
          (aux  (first coll) (next coll)))))

    user> (doall (partition-by-once-vol (fn [v] (println v) (odd? v)) [1 2 3 5 7 8 10 11]))
    1
    2
    3
    5
    7
    8
    10
    11
    ((1) (2) (3 5 7) (8 10) (11))
...