【发布时间】:2012-11-19 17:43:59
【问题描述】:
我正在用 Haskell 编写游戏 AI,我想在指定的时间内搜索游戏状态树(即我总是希望 AI 花费 3 秒来决定要采取的行动)
我怎样才能用像 Haskell 这样的纯语言做到这一点?我希望我需要深入研究线程等,但我希望尽可能地减少它。
【问题讨论】:
-
使用timeout。
标签: haskell artificial-intelligence sleep thread-sleep
我正在用 Haskell 编写游戏 AI,我想在指定的时间内搜索游戏状态树(即我总是希望 AI 花费 3 秒来决定要采取的行动)
我怎样才能用像 Haskell 这样的纯语言做到这一点?我希望我需要深入研究线程等,但我希望尽可能地减少它。
【问题讨论】:
标签: haskell artificial-intelligence sleep thread-sleep
一个想法:将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 不分配任何内存,因此对于大量内存不会立即中断。
更新:我基于这些想法创建了一个小型库,该库定义了一个用于计算的单子,该单子可以在返回最终结果或因超时而死之前返回部分结果。见https://github.com/ppetr/timeout-with-results
【讨论】:
由于您要寻找的结果取决于时间,这不可避免地会涉及不纯的代码。
看起来 base 包中的 System.Timeout 提供了运行 I/O 计算直到超时(根据 leftaroundabout 的评论)的能力。所以你需要做的就是编写一个 IO 计算返回您感兴趣的结果。但是......棘手的部分是使 IO 操作实际上 compute 结果,而不仅仅是返回未评估的结果。为此,我认为您需要来自Control.Exception 的evaluate。
【讨论】:
System.Timeout 是要走的路。它有一个非常干净的界面,一点也不觉得你在搞乱线程。
搜索游戏空间听起来像是纯粹的计算,System.Timeout 运行 IO 动作,因此您需要将值包装在 return 中,并严格评估代码以使 timeout 不会立即相信答案一经评估就已返回给 WHNF。
有一个示例 here 来自我遇到的类似问题。
【讨论】: