Haskell - 从文件中读取图形规范

use*_*786 2 haskell graph

我正在努力弄清楚如何在Haskell中读取(以及如何表示)图形.

来自文件的输入看起来像

NODES 3
EDGE 1 2
EDGE 1 3
EDGE 2 3
Run Code Online (Sandbox Code Playgroud)

我已经想出如何使用以下方法从文件中获取各行输入:

loadFile :: String -> IO [[String]]
loadFile filename = do 
                contents <- readFile filename
                return $ map words $ lines contents
Run Code Online (Sandbox Code Playgroud)

这给出了如下输出:

loadFile "input.txt"
[["NODES","3"],["EDGE","1","2"],["EDGE","1","3"],["EDGE","2","3"]]
Run Code Online (Sandbox Code Playgroud)

我真正需要弄清楚的是如何将这个图形数据表示为图形.我正在考虑将其设置为边缘列表:

type Edge = (Int,Int)
type Graph = [Edge]
Run Code Online (Sandbox Code Playgroud)

但后来我不确定如何开始实现我需要的功能,如addNode,addEdge,getNodes,getEdges.

任何帮助或指向我正确的方向将是真棒!注意:我不能使用任何已经开发的图形模块.

所以,对于tl; dr版本:

  1. 我在数据中读取最好的方法吗?
  2. 我应该如何在haskell中表示这些数据?
  3. 如果我使用上面概述的数据结构,我将如何实现其中一个功能.

J. *_*son 7

这里有很多有趣的问题.让我攻击他们.

  1. 您正在阅读数据,这对于面向行的语言来说非常合适.稍后您将看到Data.ByteStringData.Text替换String效率.你也会看到Parsec解析.但是,他们可以等待.及时重温它们.

  2. 你的图表表示没问题.邻接列表是一种常见且有用的表示.

  3. 现在,你真正的伎俩就在这里.我们来看看addNodeaddEdge.每个都是一个有点挑战性的函数,用纯函数语言生成,因为他们想要修改图...但我们没有状态.

修改无状态最重要的方法是变异.因此,您正在寻找的功能类型

addNode :: Node -> Graph -> Graph
Run Code Online (Sandbox Code Playgroud)

返回Graph的地方与输入Graph相同,只有一条边.你应该立即注意到这里有什么问题---邻接列表假设没有孤立节点.我们不能只向图表添加单个节点.

有两种解决方案.一,我们可以将节点"链接"到图形(实际上是addEdge伪装)或两个我们可以扩展图形表示以包括孤立节点.我们来做(2).

data Graph = Graph [Edge] [Int] -- orphans
Run Code Online (Sandbox Code Playgroud)

现在让我们实现添加边缘.假设您可以有重复的边,向邻接列表添加边很容易,只需附加它

addEdge0 :: Edge -> Graph -> Graph
addEdge0 e (Graph adj orph) = Graph (e:adj) orph
Run Code Online (Sandbox Code Playgroud)

但这还不够好 - 我们希望我们的孤儿列表只包含真正的孤立节点.我们会过滤它.

addEdge :: Edge -> Graph -> Graph
addEdge (n1,n2) (Graph adj orph) = 
  Graph ((n1,n2):adj) (filter (/=n1) . filter (/=n2) $ orph)
Run Code Online (Sandbox Code Playgroud)

getEdges 因为我们已经存储了边缘列表,所以是微不足道的

getEdges :: Graph -> [Edge]
getEdges (Graph edges _) = edges
Run Code Online (Sandbox Code Playgroud)

getNodes只需要将邻接列表中的所有节点附加到孤立列表.我们可以使用Data.List.nub只获取唯一的节点.

getNodes :: Graph -> [Int]
getNotes (Graph adj orph) = nub (orph ++ adjNodes adj) where
  adjNodes [] = []
  adjNodes ((n1,n2):rest) = n1 : n2 : adjNodes rest
Run Code Online (Sandbox Code Playgroud)

希望这些能为您提供一些如何用功能语言思考的指示.你会花点时间去深入了解它们是如何工作的,但我在这里介绍了大量有趣的概念.

这里的后续步骤可能包括尝试使用Statemonad重新捕获命令状态修改并将这些Graph修改函数链接在一起.