1

我有一个data type G,其中有字段_repr :: Data.Graph.Inductive.Gr String String。通常的方式,当向Gr图中添加新节点时,我们必须提供一个LNode a对象,该对象基本上定义为 的元组(Int, a),其中 Int 是 Graph 中的节点索引 - 请参见add下面的示例函数。

我想实现一个函数addx,它将自动计算索引(例如通过使用Data.Graph.Inductive.newNodes函数)。我希望addx有 的签名,addx :: String -> G -> Int这个函数将计算新的自由索引,修改图 G 并返回这个计算的索引。G在 Haskell 中是否可以通过使用镜头或类似的东西来创建这样的功能(这将修改现有对象 -在这种情况下)?

我已经看到,Haskell lens 被定义为lens :: (a -> c) -> (a -> d -> b) -> Lens a b c d和 lens 基本上是一个“getter”和“setter”,所以它的签名允许不同类型的 getter 输出(c)、setter 值(d)和 setter 输出(b)。

import qualified Data.Graph.Inductive     as DG

data G = G { _repr :: DG.Gr String String, _name::String} deriving ( Show )

empty :: G
empty = G DG.empty ""

add :: DG.LNode String -> G -> G
add node g = g{_repr = DG.insNode node $ _repr g}

-- is it possible to define it?
addx :: String -> G -> Int
addx name g = undefined

main :: IO ()
main = do
    let g = add (1, "test2")
          $ add (0, "test1")
          $ empty

        n1 = addx "test2" g
        g2 = DG.insEdge(n1,0)
           $ DG.insEdge(0,1)

    print $ g
4

1 回答 1

2

您的 for 类型addx已损坏,因为您无法G在不返回修改后的形式(如addx1 :: String -> G -> (Int, G). 如果你对 Haskell monad 有一个聪明的眼光,你可能会注意到它有一个同构类型,addx2 :: String -> State G Int.

我们可以将一切都与这种“有状态”的方向对齐

add' node = do g <- get
               put $ g { _repr = DB.insNode node $ _repr g }

并用镜头让它更简洁

add'' node = repr %= DB.insNode node

归根结底,这里真正的挑战是跟踪节点身份。一种方法是将它与repr您的类型一起携带

data G = G { _repr :: DG.Gr String String, _name :: String, _index :: Int } 
empty = G DG.empty "" 0

然后在构建节点时使用它(再次使用镜头!)

addx' name = do i <- use index
                repr %= DB.insNode (i, node)
                i += 1
于 2013-07-22T20:13:40.453 回答