我正在Haskell中编写游戏AI,我想在游戏状态树中搜索指定的时间(即我总是希望AI花3秒钟来决定要做什么)< / p>
我怎么能用像Haskell这样的纯语言来做这件事?我希望我需要做一些潜入线程等,但我宁愿尽量减少这一点。
答案 0 :(得分:5)
一个想法:将timeout
(由@MathematicalOrchid建议)与来自SafeSemaphore的可变变量结合起来,以便在每次进程计算部分结果时存储中间值:
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
不分配任何内存,因此对于大数字,它不会立即中断。
更新我根据这些想法创建了一个小型库,这些想法定义了可以在返回最终结果或在超时时死亡之前返回部分结果的计算monad。见https://github.com/ppetr/timeout-with-results
答案 1 :(得分:3)
由于你要找的结果取决于时间,这不可避免地会涉及不纯的代码。
看起来System.Timeout
包中的base
提供了在某些超时之前运行I / O计算的能力(根据左下角的注释)。所以你需要做的就是写一个{ {1}}计算返回您感兴趣的结果。但是......棘手的部分是让IO
动作实际计算结果,而不仅仅是返回未评估的结果。为此,我认为需要来自IO
的{{1}}。
答案 2 :(得分:2)
System.Timeout
是要走的路。它有一个非常干净的界面,你根本不会感觉像是在搞乱线程。
搜索游戏空间听起来像纯计算,System.Timeout
运行IO
个动作,因此您需要将值包装在return
中,并严格评估代码足以让timeout
一旦认为答案已经被WHNF评估,就不会立即回复。
从我遇到的类似问题中可以看出代码可能是什么样的here。