如何使用 Haskell 超时函数(在 System.Timeout 中)来停止失控计算?

Posted

技术标签:

【中文标题】如何使用 Haskell 超时函数(在 System.Timeout 中)来停止失控计算?【英文标题】:How can I use the Haskell timeout function (in System.Timeout) to halt runaway computations? 【发布时间】:2019-08-15 15:27:23 【问题描述】:

System.Timeout 中的超时功能有时无法停止无限计算。

例如,

timeout 1000 $ print $ length [0..]

按预期返回Nothing,因为超时中断了无限计算。但是

timeout 1000 $ print $ length $ cycle [1,2,3]

永远循环。

这是在 Mac 上,使用 ghc 或 ghci 8.6.4。

我希望第二个示例的行为与第一个示例一样,在 1 毫秒后中断无限计算并返回 Nothing。相反,第二个示例挂起。

【问题讨论】:

是的,不幸的是,不分配内存的紧密循环不能被timeout 中断。 GHC 假装它是抢占式线程,而且外观非常好,但实际上在每个 OS 线程内它都是协作线程。分配——通常是不可变语言中非常常见的事件——是选择是否让出控制权的触发器。但是 GHC 足够聪明,能够通过观察length 的内部计数器可以就地更新(并且cycle [1,2,3] 及时分配所需的数量,将这个循环优化为不分配的循环,似乎)。 查看Bugs in GHC的第一个条目。这是一个长期存在的问题,如果不禁用大量优化,就不容易解决。 @chi 不错!如果您的链接中建议的 -fno-omit-yields 有效,那么似乎值得写一个(简短的)答案来建议它。 @DanielWagner 这对我来说似乎相当脆弱,它是模块范围的而不是表达式范围的。另外,我想,Will Ness 下面发现了一个重复项。 嗯。似乎相关,尽管它侧重于停止无限循环的交互式而非编程方式。 【参考方案1】:

您可以使用自己的非共享实现 cycle

> _Y g = g (_Y g)
_Y :: (t -> t) -> t

> take 10 . _Y $ ([1,2,3] ++)
[1,2,3,1,2,3,1,2,3,1]
it :: Num a => [a]

> timeout 100000 . print . length . _Y $ ([1,2,3] ++)
Nothing
it :: Maybe ()
(0.11 secs, 152470624 bytes)

_Y 当然会分配一个无限增长的列表,不像共享cycle 相当于fix ([1,2,3] ++) 在内存中创建一个实际的循环列表:

> timeout 100000 . print . length . fix $ ([1,2,3] ++)
<<<hangs>>>

另见:

How can I stop infinite evaluation in GHCi?

【讨论】:

以上是关于如何使用 Haskell 超时函数(在 System.Timeout 中)来停止失控计算?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用“ord”函数在 Haskell 中将 Char 转换为 Int?

如何正确使用haskell中的长度函数?

在 .NET 中调用 Haskell 函数

如何在 Haskell 中比较镜头

如何在 haskell 中打印列表?

如何在Haskell中创建函数minimax?