模仿Python产量的惯用Clojure方法

aar*_*acy 6 clojure

我正在遍历列表,逐步建立状态,偶尔遇到某个哨兵时,我会返回结果。如果我是在Python中执行此操作,则我会懒惰地返回yield结果,并在执行时跟踪函数本地范围内的状态:

# this is simplified for illustration
def yielder(input_list):
    state = 0
    for item in input_list:
        if item = 'SENTINEL':
            yield state * 2
            state = 0
        else:
            state += item

yielder([1, 5, 2, 5, 'SENTINEL', 4, 6, 7]) # [26, 34]
Run Code Online (Sandbox Code Playgroud)

我的第一个实现使用reduce,但这并不如yield

  • 我在两次迭代之间传递的值既具有循环状态,又具有要产生的项,这似乎很笨拙
  • 不偷懒

iterate 可以用来减轻后者,但我实际上并不希望为每个输入项都返回一些东西,因此需要更多的调整。

Clojure中的惯用方式是什么?

Art*_*ldt 5

使用懒惰-SEQ你提到你可以建立这个自己或者你可以使用partitionreduce分割问题转化阶段,然后拧在一起。我将使用thread-last宏单独显示每个步骤:

user> (->> [1, 5, 2, 5, :SENTINEL, 4, 6, 7] ;; start with data
           (partition-by #(= :SENTINEL %))  ;; ((1 5 2 5) (:SENTINEL) (4 6 7))
           (take-nth 2)                     ;; ((1 5 2 5) (4 6 7))
           (map #(* 2 (reduce + %))))       ;; the map here keeps it lazy
(26 34)
Run Code Online (Sandbox Code Playgroud)

这是直接在lazy-seq中使用:

user>  (defn x [items]
         (when (seq items)
           (lazy-seq (cons (* 2 (reduce + (take-while #(not= :SENTINEL %) items)))
                           (x (rest (drop-while #(not= :SENTINEL %) items)))))))
#'user/x
user> (x [1, 5, 2, 5, :SENTINEL, 4, 6, 7])
(26 34)
Run Code Online (Sandbox Code Playgroud)

  • 最好将#(=:SENTINEL%)写为## :: SENTINEL} (6认同)