假设我想在 Haskell 中构建一个iterate step x0
列表,但具有包含终止条件。所以在 Python 中,这将list(my_gen)
def my_gen():
x = x0
while not done(x):
x = step(x)
yield x
(编辑:如果我想包含x0
,这应该在循环之前有一个额外的yield x
。
一种方法是写我自己的takeWhileInclusive并说
takeWhileInclusive (not . done) . iterate step x0
这是实现此目的的哈斯克尔方式(或哈斯克尔方式)吗?当done x
为真时,试图为step x
附加一些哨兵值,然后使用takeWhile
,这似乎是不自然的。
特别是,我正在考虑LeetCode上存在最多水问题的容器,并用类似的东西来解决它
maxWith volume . smartSteps (0, n)
where smartSteps = takeWhileInclusive ((i,j) -> j - i > 1) . iterate step
step
增加i
或减少j
(或两者兼而有之),根据哪个指数具有更高的线。
当然,在这里很容易使用takeWhile
j> i,但我想思考如何处理没有自然"你走得太远"条件的情况,只是一个"你完成了"的条件。
编辑:这个问题已被标记为重复(我在问题中链接到的问题),但事实并非如此。问题不在于如何写takeWhileInclusive
,事实上,这个问题明确地将takeWhileInclusive
视为给定的。它是关于如何完成可能使用或可能不会使用的任务takeWhileInclusive
.
您可以使用unfoldr
来生成序列:
unfoldr (x -> if done x then Nothing else Just (x, step x)) x0
例如
> import Data.List
> step = (+1)
> done = (> 10)
> x0 = 0
> unfoldr (x -> if done x then Nothing else Just (x, step x)) x0
[0,1,2,3,4,5,6,7,8,9,10]
unfoldr
在启动x0
调用其函数。当函数返回Nothing
时,unfoldr
停止。当函数返回Just (x, y)
时,它会将x
附加到结果中,并在y
时再次调用该函数。
将您的生成器与 Python 实现进行比较unfoldr
:
def unfoldr(f, x):
while True:
if (y := f(x)) is None:
return
else:
yield y[0]
x = y[1]
list(unfoldr(lambda x: None if done(x) else (x, step(x)), x0))
是的,这是一种哈斯克尔式的方式。
另一种 Haskell-y 方法(或实现takeWhileInclusive
的 Haskell-y 方法)是稍后一步压缩iterate
d 值。
myGen done step = map snd . takeWhile (not . done . fst) . ap zip tail . iterate step
注意:与iterate
不同(但与my_gen
一样),它不会发出初始x
值作为步骤之一。
你可以定义
takeUntil done xs =
foldr (x r -> if done x then [x] else x : r) [] xs
然后像这样使用它
takeUntil done $ iterate step x0
例如takeUntil (> 9) [1..] == [1..10]
.
用foldr
指定最终元素很容易(如此处所示),但使用编码"变形"的unfoldr
更麻烦,用空列表作为哨兵关闭生成的列表。使用"同态"可以指定非空尾,这似乎是此任务的合适工具。