模仿Python的惯用Clojure方式';s产量

模仿Python的惯用Clojure方式';s产量,clojure,Clojure,我在一个列表中迭代,边走边建立状态,偶尔当我遇到某个哨兵时,我会返回一个结果。如果我是在Python中这样做的,我会懒洋洋地生成结果,并在运行时跟踪函数的局部作用域中的状态: # this is simplified for illustration def yielder(input_list): state = 0 for item in input_list: if item = 'SENTINEL': yield state * 2

我在一个列表中迭代,边走边建立状态,偶尔当我遇到某个哨兵时,我会返回一个结果。如果我是在Python中这样做的,我会懒洋洋地
生成结果,并在运行时跟踪函数的局部作用域中的状态:

# 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中实现这一点的惯用方法是什么?

您可以使用您提到的lazy seq自己构建它,或者您可以使用
分区
减少
将问题分为多个阶段,然后将它们连接在一起。我将使用thread last宏单独显示每个步骤:

user>(->[1,5,2,5,:SENTINEL,4,6,7];从数据开始
(按#(=:哨兵%);(:哨兵)(1 5 2 5)(:哨兵)(4 6 7))划分)
(以第二节为例);((1 5 2 5)(4 6 7))
(地图#(*2(减少+)));;这里的地图使它保持懒惰
(26 34)
这里直接使用lazy seq:

user>(定义x[项目]
(时间(序号项目)
(惰性顺序(缺点(*2(减少+(占用时间#(非=:哨兵%)项)))
(x(休息(在#(非=:哨兵%)项(#)(#)(#))(#(非#:
#'用户/x
用户>(x[1,5,2,5,:哨兵,4,6,7])
(26 34)

这是我使用
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]

虽然我更喜欢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))))
有一种方法可以使用模仿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)

请注意,最初的问题描述有一个bug,因为累积状态仅在遇到
:SENTENEL
标记时输出。
data-1
data-2
的不同输出说明了问题。

如果我只返回
(cons-yield val(yielder…)
每当我遇到哨兵时,只要
(yielder…)我想a可能会给我想要的东西
否则,
将不起作用,因为循环在到达
时终止:虽然
条件。哦,我误解了,我将编写一个不同的示例。这回答了问题,谢谢!虽然它在语义上略有不同,因为原始算法将项相加并乘以2。
#(=:SENTINEL%)
最好写成
{:SENTINEL}
懒惰seq的要点是懒惰,这一点在这里已经丢失了。在
(interpose:SENTINEL(range))
上尝试Arthur的版本和您的版本,使用
take
仅获取前几个元素。哦,我明白了。我只想到非惰性输入。然后,我将+1亚瑟的。