如何解决Haskell 列表理解 - 并非所有数据都被传输
我有这样的网络数据结构:
data Network = Empty | Node Double [Network] [Network]
其中 Double 代表节点标签,第一个列表是父节点,第二个列表是给定节点的子节点。假设所有边都是从根到叶的,并且没有循环。
data Edge = Edge (Network,Network)
边从 a->b 指向 Edge (a,b)
我编写了一个函数,用于列出网络的所有树边。为了进一步定义这一点,具有多个父节点(网状节点)的节点的所有父(传入)边都不是树边。我正在考虑来自网状节点的输出边以及网络中的所有其他边是树边。
我尝试像这样使用列表理解来完成这个任务:
getTreeEdges :: Network -> [Edge]
getTreeEdges (Node label ps cs) = nub (getTreeEdgesHelp (Node label (Empty:[]) cs))
getTreeEdgesHelp :: Network -> [Edge]
--leaf node case,children is empty list
getTreeEdgesHelp n@(Node _ ps []) = [Edge (p,n) | p <- ps ]
--reticulation node and root case,there is more than one parent or parent is empty(root)
getTreeEdgesHelp (Node _ ps cs) | ((length ps) > 1||ps==(Empty:[])) = (concat (map getTreeEdgesHelp cs))
--interal node case
getTreeEdgesHelp n@(Node _ ps cs) = ([Edge (p,n) | p <- ps ])++( concat (map getTreeEdgesHelp cs))
如您所见,列表推导式用于存储叶子和内部边的父边,而根节点和网状节点则跳过向列表添加边并进行递归。叶结束递归。
我遇到的问题是存储的边不包含所有预期信息。更具体地说,父节点的父信息消失了。其他信息仍然存在,标签和子项不受影响。
例如,假设我存储边 (a,b)
。网络 b
存储完全正确。网络 a
有它自己(根节点)和所有后代节点都存在并正确标记,但它们都没有其父节点的任何条目,每个节点的父节点都是 []
。
这里是一个给定的输入、输出和预期输出的示例,具有 newick 格式 (a,b)c,用于具有子节点 a 和 b 的节点 c。我还在标签后的方括号中包含给定节点的父节点。为了全面披露,以下是我为 Show
和 Network
定义 Edge
的方式。
instance Show Network where
show Empty = "_"
show (Node a ps []) = (show a)++"["++printlabels ps++"]"
show (Node a ps [x,y]) = "("++show x++","++show y ++ ")"++(show a)++"["++printlabels ps++"]"
show (Node a ps [x]) = "("++show x++")"++(show a)++"["++printlabels ps++"]"
printlabels :: [Network] -> [Char]
printlabels [] = " "
printlabels (Empty:_) = "root"
printlabels ((Node label _ _):ns) = (show label)++","++(printlabels ns)
这是我生成的示例树。因为标签比较长,我用字母代替了内部节点的标签,用整数代替了叶子的标签。
(((4.0[c],((0.0[f])f[e,h],(1.0[g],2.0[g])g[e])e[c])c[h],(0.0[f])f[e,h])h[d],3.0[d])d[root]
运行上述函数给出:
[(d,h),(h,c),(c,4.0),e),(f,0.0),(e,g),(g,1.0),2.0),(d,3.0)]
与
instance Show Edge where
show ( Edge ((Node labelu _ _),(Node labelv _ _)) ) = "("++(show labelu)++","++(show labelv)++")"
当我查询单个边时,说出位置 5 (e,g)
处的边,然后将 Show
重写为 Edge
以
instance Show Edge where
show ( Edge (u,v) ) = "("++(show u)++"=1,2="++(show v)++")"
我得到 (((1.0[ ],2.0[ ])g[ ],0.0[ ])e[ ]=1,2=(1.0[g],2.0[g])g[e])
而预期的结果是 (((1.0[g],2.0[g])g[e],h])e[c]=1,2.0[g])g[e])
我注意到的另一个错误是 0.0
被指定为 g
的兄弟,这是不正确的。正如评论者指出的那样,我在尝试获取树边时没有更改任何节点,我只是将节点分配给适当的边对并将其存储在列表中。您还可以看到我输入的网络具有正确的出身和结构。
这里会发生什么?我是 Haskell 的新手,我使用列表理解错误吗?
解决方法
对于纯语言而言,您的数据结构非常不寻常,并且将被证明非常难以使用。尽管您表示的图形是非循环的,但您用来表示它的数据结构是循环的:父节点的子节点又是节点。这意味着当你修改某个东西时,你需要循环修改它:如果我改变一个节点,我也必须改变相应的孩子的父级,以及它的每个子级的父级等等。这是非常复杂和微妙的。
这个挑战是纯语言独有的:改变循环数据结构对于可变数据结构来说不是问题——你可以只在一个地方改变它,它被引用的任何地方都可以“看到”改变。
最好是找到图形的非循环表示。例如:图是边的列表。如果您需要它,您还可以包含一组节点(因此可以表示度数为 0 的节点)。仅通过标签引用节点,当您需要有关它们的信息时,请在列表中查找它们。
另一种方法是inductive graph (在 fgl 库中实现)。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。