相当于具有停止条件的发电机的Haskell



假设我想在 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(或两者兼而有之),根据哪个指数具有更高的线。

当然,在这里很容易使用takeWhilej> 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 方法)是稍后一步压缩iterated 值。

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更麻烦,用列表作为哨兵关闭生成的列表。使用"同态"可以指定非空尾,这似乎是此任务的合适工具。

最新更新