Python中Haskell的“跨度”的延迟递归

Posted

技术标签:

【中文标题】Python中Haskell的“跨度”的延迟递归【英文标题】:Lazy recursion for Haskell's `span` in Python 【发布时间】:2021-12-06 21:45:23 【问题描述】:

有人可以建议 Haskell span 函数的 Python 实现吗?

span :: (a -> Bool) -> [a] -> ([a],[a]) 
span p [] = ([],[]) 
span p (x:xs) = if p x then (x:ys,zs)
                else ([],x:xs) 
                where (ys,zs) = span p xs

我希望它在每个参数中都是惰性的,所以是两个生成器的列表。如果它是递归实现的,我更喜欢它,以了解在列表尾部递归的 Python 定义。如果合适的话,也许是yield from

【问题讨论】:

除了 amalloy 的答案中提出的优秀观点之外,还值得注意的是 Python 和 Haskell 中的列表非常不同。 Haskell 列表是链表,非常适合递归并且非常适合 Haskell。 Python 列表实际上只是顺序数组(类似于 C++ 中的 std::vector 或 Java 中的 ArrayList),因此如果不每次都复制几乎整个列表,就无法真正获取列表的“尾部”。 Python 程序员很少使用链表,因为它对范式毫无意义。 基于数组的列表和惰性不能很好地结合使用,因为这样的列表不是递归数据结构并且本质上是严格的。您需要一个命令式构造(生成器)才能懒惰地构造它。 【参考方案1】:

我希望它在每个参数中都是惰性的,所以是两个生成器的列表。

你真的不能这样做。生成器和惰性列表之间有很大的区别:后者被缓存,因此您可以重用它的值。在这种情况下,考虑 Haskell 实现,其中(xs, ys)span f zs 的结果。当您第一次查看xs 时,您需要浏览该列表,这会触发对span f zs 的更多评估...最后,在xsys 的末尾会发现更多内容。您不能使用生成器来执行此操作,因为从其中一个读取会影响另一个。

我在回复Lazy partition-by 时对此进行了更多讨论。

【讨论】:

我猜 OP 可以创建一个 Thunk 类型,它在评估时共享其结果(例如,通过破坏性更新)。 Thunk 可以实现 applicative/monad 以使显式 thunk 调用再次隐式。

以上是关于Python中Haskell的“跨度”的延迟递归的主要内容,如果未能解决你的问题,请参考以下文章

为啥haskell中的递归列表这么慢?

如何在 Haskell 函数中对递归数据类型进行递归

在haskell中记忆多维递归解决方案

Haskell 对字符串中的字符进行递归

如何在 Haskell 递归调用中打印迭代?

在 haskell -> TRIE 中使用自定义递归数据类型