7

我正在用 Haskell 编写游戏 AI,我想在指定的时间内搜索游戏状态树(即我总是希望 AI 花费 3 秒来决定要采取什么行动)

我怎样才能用像 Haskell 这样的纯语言来做到这一点?我希望我需要深入研究线程等,但我希望尽可能地减少它。

4

3 回答 3

5

一个想法:将(由@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

于 2012-12-01T21:55:24.653 回答
3

由于您要寻找的结果取决于时间,这不可避免地会涉及不纯的代码。

System.Timeout从包中看起来,它base提供了运行 I/O 计算直到某个超时的能力(根据 leftaroundabout 的评论。)所以你需要做的就是编写一个IO返回你感兴趣的结果的计算。但是.. . 棘手的部分是让IO动作实际计算结果,而不仅仅是返回未评估的结果。为此,我认为您需要evaluateControl.Exception.

于 2012-12-01T20:43:38.013 回答
2

System.Timeout是要走的路。它有一个非常干净的界面,一点也不觉得你在搞乱线程。

搜索游戏空间听起来像是纯粹的计算,并System.Timeout运行IO动作,因此您需要将值包装在 中return,并足够严格地评估代码,timeout以免在评估到 WHNF 后立即相信答案已返回.

这里有一个代码可能是什么样子的示例,来自我遇到的类似问题。

于 2012-12-01T21:48:22.747 回答