我正在用 Haskell 编写游戏 AI,我想在指定的时间内搜索游戏状态树(即我总是希望 AI 花费 3 秒来决定要采取什么行动)
我怎样才能用像 Haskell 这样的纯语言来做到这一点?我希望我需要深入研究线程等,但我希望尽可能地减少它。
我正在用 Haskell 编写游戏 AI,我想在指定的时间内搜索游戏状态树(即我总是希望 AI 花费 3 秒来决定要采取什么行动)
我怎样才能用像 Haskell 这样的纯语言来做到这一点?我希望我需要深入研究线程等,但我希望尽可能地减少它。
一个想法:将(由@MathematicalOrchid 建议)与来自SafeSemaphoretimeout
的可变变量结合起来,以在您的进程每次计算部分结果时存储中间值:
import Control.Monad
import Control.Concurrent.MSampleVar
import System.Timeout
fac :: Integer -> Integer
fac 0 = 1
fac n = n * fac (n - 1)
tmo :: Int -> ((a -> IO ()) -> IO ()) -> IO (Maybe a)
tmo ms f = do
mvar <- newSV Nothing
timeout ms (f (writeSV mvar . (Just $!)))
readSV mvar
longComp :: (Integer -> IO ()) -> IO ()
longComp save = let loop n = save (fac n) >> loop (n + 1)
in loop 0
main :: IO ()
main = tmo 10000 longComp >>= print
传递给函数tmo
的第一个参数是一个IO
动作,它可以用来保存中间结果。如果超时,则返回最后保存的结果。结果被转换为 WHNF 以便真正的计算发生在保存结果的线程中,而不是在从tmo
.
在这个变体中,传递给的函数tmo
必须保存它的输出,它不能返回它。但是很容易修改它,使其签名为(a -> IO ()) -> IO a
.
如果你想让事情变得更纯粹,我建议创建你自己的 monad 来封装这个想法而不会泄露IO
。
更新:请注意:
尽管运行时会努力确保不会发生任意延迟,但不能保证异常会及时传递。在 GHC 中,只有当线程到达安全点时才会引发异常,安全点是发生内存分配的地方。一些循环不会在循环内执行任何内存分配,因此不会被 throwTo 中断。
(来自throwTo的文档)。在上面的示例中,fac
不分配任何内存,因此对于大量内存不会立即中断。
更新:我基于这些想法创建了一个小型库,该库定义了一个用于计算的单子,该单子可以在返回最终结果或因超时而死之前返回部分结果。见https://github.com/ppetr/timeout-with-results
由于您要寻找的结果取决于时间,这不可避免地会涉及不纯的代码。
System.Timeout
从包中看起来,它base
提供了运行 I/O 计算直到某个超时的能力(根据 leftaroundabout 的评论。)所以你需要做的就是编写一个IO
返回你感兴趣的结果的计算。但是.. . 棘手的部分是让IO
动作实际计算结果,而不仅仅是返回未评估的结果。为此,我认为您需要evaluate
从Control.Exception
.
System.Timeout
是要走的路。它有一个非常干净的界面,一点也不觉得你在搞乱线程。
搜索游戏空间听起来像是纯粹的计算,并System.Timeout
运行IO
动作,因此您需要将值包装在 中return
,并足够严格地评估代码,timeout
以免在评估到 WHNF 后立即相信答案已返回.
这里有一个代码可能是什么样子的示例,来自我遇到的类似问题。