使用Monad / ST在可变图中传递非并发消息

时间:2014-09-11 00:07:39

标签: haskell state st-monad

我正在尝试针对以下情况制定数据结构。

图表结构

我计划得到一个带有未加权有向边的节点图:Graph = [Node]

每个节点都有:

  1. 某些TBD内部(持久)状态
  2. 传入消息的队列
  3. 它可以发送的一种消息,由接受的函数决定 当前节点状态(可能失败)
  4. 边缘列表
  5. Node { nodeState :: NodeState, inbox :: Queue NodeMessage, nodeMessage :: (NodeState -> Maybe NodeMessage), connections::[NodeEdge] }

    每个边缘都是捕获目标节点的待处理消息的中间步骤

    NodeEdge { pendingMessage:: Maybe NodeMessage, targetNode :: Node }

    消息传递

    消息传递分阶段进行并且不是连续的(尽管队列可以并行处理以减少计算时间)。

    • 阶段1:检查每个节点的收件箱,处理所有消息并更新NodeState(如果相关)。
    • 阶段2:让每个节点都运行nodeMessage,如果这导致Just NodeMessage,则将NodeMessage发送到每个连接([NodeEdge]
    • 阶段3:检查每个节点边缘,如果有消息,将其添加到目标节点的消息队列中。

    Monat / ST

    我最初的计划是为每个节点分配一个ID(可能是一个简单的Int),并将每个节点存储在Map Int Node中。我之前没有尝试过ST Monad,但我想我可以使用像ST s (M.Map Int Node)这样的东西。对于任何给定阶段,每个节点的消息发送活动可以在O(k log N)中处理。

    另一方面,如果节点/边缘能够更新其边/节点的可变状态,则可以在O(k)中处理任何单个队列。

    虽然ST / Map方法看起来相当直观,但整个图形的可变性超出了我的范围。

    任何建议/提示/推荐阅读?

1 个答案:

答案 0 :(得分:1)

我不打算将这个答案标记为正确,因为它并没有真正回答这个问题。然而,这是我的解决方案。

因为我图中的节点数永远不会改变,所以我意识到我可以使用数组。我实际上正在重新考虑使用可变数据类型 - 即使我得到一个更简单的工作流程更新数组我得到的懒惰的好处更少,我最终编写了大量的命令式样式代码。我实际上在考虑使用Array和State Monad,而不是ST。

这是我使用STArray编写的一些测试代码。 A"适当"回答这个问题的是一个专门针对Graphs的类似数据类型 - 也许有一个STGraph库?

无论如何 - 这是使用STArray的示例代码:

import Control.Monad.ST
import Data.Array.ST
import Data.Array

import qualified Data.Dequeue as DQ

type Id = Int

data Node = Node {
    nodeId :: Id,
    nodeState :: NodeState,
    nodeInbox :: DQ.BankersDequeue NodeMessage,
    nodeMessage :: (NodeState -> Maybe NodeMessage),
    connections :: [NodeEdge] }

instance Show Node where
    show x = "Node: " ++ (show . nodeId $ x) ++ " :: Inbox: " ++ (show . nodeInbox $ x) ++ " :: " ++ (show . connections $ x)

data NodeEdge = NodeEdge { pendingMessage:: Maybe NodeMessage, targetNode :: Id } deriving Show

data NodeState = NodeState { stateLevel :: Int } deriving Show

data NodeMessage = NodeMessage { value :: Int } deriving Show

es = [[NodeEdge Nothing 1,NodeEdge Nothing 2],[NodeEdge Nothing 0,NodeEdge Nothing 2],[NodeEdge Nothing 0,NodeEdge Nothing 1]]
ns = take 3 $ map (\x -> Node x (NodeState 0) (DQ.fromList []) (\_ -> Nothing) (es !! x)) $ [0,1..]

testArray :: Array Int Node
testArray = listArray (0,2) ns

testSTarr = do  arr <- newListArray (0,2) ns :: ST s (STArray s Int Node)
                a <- readArray arr 1
                let i = targetNode . head $ connections a
                b <- readArray arr i
                let m = NodeMessage 2
                    ms = DQ.pushBack (nodeInbox b) m
                    b' = b { nodeInbox = ms }
                writeArray arr (nodeId b) b'
                return arr

testSTarr' x = do a <- readArray x 0
                  return a

bp = testSTarr >>= testSTarr'

main = do
            print $ runST bp 
            return ()