我正在嘗試撰寫一個生成器函式(或實作等效的函式),它xs在 Python 中使用可迭代物件并計算“運行”次數。(這是Bird的Haskell Functionally Thinking Functionally的一個問題,我想使用Python的懶惰特性將其轉換為Python。)所以
list(iter(count_runs(['a', 'a', 'b', 'c', 'a', 'd', 'd'])))
# => [(2, 'a'), (1, 'b'), (1, c'), (1, 'a'), (2, 'd')]
在 Haskell 中
countRuns :: [a] -> [(Int, a)]
countRuns [] = []
countRuns x:xs = (1 length us, x):countRuns vs
where us, vs = span (==x) xs
在 Python 中,我想寫一些類似的東西
from itertools import takewhile, dropwhile
def count_runs(xs):
# get first element x of xs, if it exists
us, vs = (takewhile(lambda y: y==x, xs),
dropwhile(lambda y: y==x, xs))
yield (1 len(list(us)), x)
yield from count_runs(vs)
但問題是,vs是一個迭代器了,所以我會遇到麻煩,如果我把takewhile和dropwhile它在未來的遞回。(當我呼叫list(takewhile(..., xs))下一個遞回時,它也會去掉第一個元素dropwhile(..., xs),因為它們都在看同一個迭代器。
我該如何解決這個問題,獲取第二行第一個元素的正確方法是什么?
uj5u.com熱心網友回復:
span和之間的顯著差異takewhile是takewhile消耗第一個非x值以確定何時停止產生值。結果,您將丟失輸入中的所有單例項;特別是,在產生領先的s集時takewhile失去了第一個。迭代器協議無法查看迭代器的下一個元素,也無法放回它消耗的元素。ba
相反,您需要兩個獨立的迭代器:一個用于takewhile生成所需的前綴,另一個用于為遞回呼叫洗掉該前綴。
def count_runs(xs):
try:
x = next(xs)
except StopIteration:
return
t1, t2 = tee(xs)
us = list(takewhile(lambda y: y == x, t1))
yield (1 len(us), x)
yield from count_runs(dropwhile(lambda y: y == x, t2))
(請注意,該itertools檔案將span其配方部分中的類似before_and_after功能實作為函式。它不使用tee,但我請您參閱實際實作以了解詳細資訊。)
def before_and_after(xs):
...
def count_runs(xs):
try:
x = next(xs)
except StopIteration:
return
first, second = before_and_after(lambda y: y == x, xs)
yield (1 len(list(first)), x)
yield from count_runs(second)
)
但是,大部分作業已經由itertools.groupby.
def count_runs(xs):
yield from ((len(list(v)), k) for k, v in groupby(xs))
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/330332.html
