模仿Python的惯用Clojure方式';s产量
我在一个列表中迭代,边走边建立状态,偶尔当我遇到某个哨兵时,我会返回一个结果。如果我是在Python中这样做的,我会懒洋洋地模仿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
生成结果,并在运行时跟踪函数的局部作用域中的状态:
# 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亚瑟的。