我正在遍历列表,在我去的时候建立状态,偶尔当我遇到某个哨兵时,我会返回一个结果。如果我在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]
我的第一个实现使用了reduce
,但这不如yield
那么好,因为:
iterate
可用于缓解后者,但我实际上并不想为每个输入项返回一些内容,因此需要更多的内容。
在Clojure中执行此操作的惯用方法是什么?
答案 0 :(得分:5)
您可以使用lazy-seq自己构建它,或者可以使用partition
和reduce
将问题分成几个阶段然后将它们连接在一起。我将使用线程最后一个宏来显示它自己的每一步:
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)
这里直接使用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)
答案 1 :(得分:3)
The Tupelo library可以使用模仿Python生成器函数的lazy-gen
/ yield
来实现此目的:
(ns xyz
(:require [tupelo.core :as t] ))
(def data-1 [1 5 2 5 :SENTINEL 4 6 7] )
(def data-2 [1 5 2 5 :SENTINEL 4 6 7 :SENTINEL] )
(defn yielder [vals]
(t/lazy-gen
(let [state (atom 0)]
(doseq [item vals]
(if (= :SENTINEL item)
(do
(t/yield (* 2 @state))
(reset! state 0))
(swap! state + item))))))
(yielder data-1) => (26)
(yielder data-2) => (26 34)
请注意,原始问题描述存在错误,因为累积状态仅在遇到:SENTENEL
标记时输出。 data-1
和data-2
的不同输出说明了问题。
答案 2 :(得分:0)
虽然我更喜欢Arthur的第一个解决方案,但这也可以用较低级别的风格编写,而不是使用lazy-seq:
(defn f [xs]
(loop [[x & xs :as xxs] xs, n 0, ret []]
(cond (empty? xxs) (conj ret (* n 2))
(= x :sentinel) (recur xs 0 (conj ret (* n 2)))
:else (recur xs (+ n x) ret))))
答案 3 :(得分:-1)
以下是使用reduce
的我的版本:
(def v [1 5 2 5 "SENTINEL" 4 6 7])
(defn r []
(let [{:keys [result current]}
(reduce (fn [acc x]
(case x
"SENTINEL" (-> acc
(update-in [:result] conj (* 2 (:current acc)))
(update-in [:current] (constantly 0)))
(update-in acc [:current] #(+ x %))))
{:result [] :current 0} v)]
(conj result (* 2 current))))
user> (r)
[26 34]