【发布时间】:2021-09-01 13:52:16
【问题描述】:
我有这样的网络数据结构:
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。我还在标签后的方括号中包含给定节点的父节点。为了全面披露,以下是我为Network 和Edge 定义Show 的方式。
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),(c,e),(f,0.0),(e,g),(g,1.0),(g,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],(0.0[f])f[e,h])e[c]=1, 2=(1.0[g],2.0[g])g[e])
我注意到的另一个错误是0.0 是g 的兄弟,这是不正确的。正如评论者指出的那样,在尝试获取树边缘时,我没有更改任何节点,我只是将节点分配给适当的边缘对并将其存储在列表中。您还可以看到我输入的网络具有正确的出身和结构。
这里会发生什么?我是 Haskell 的新手,我是否使用了错误的列表理解?
【问题讨论】:
-
FWIW,在数据构造函数中存储一对是非常不寻常的。
data Edge = Edge Network Network会更常见。它们并不完全相同,在几乎所有情况下,您都更喜欢没有额外配对的版本。获取您正在寻找的信息的指针少了一个。 -
您能给出您提到的简单
(a, b)示例的输入、预期输出和实际输出吗?我不清楚结构是什么样的,我觉得问题出在构建结构的方式上,而不是在此处显示的代码中,因为您根本没有更改此代码中的节点。跨度> -
Empty的目的是什么?你没有将它与你的任何函数匹配,一个空列表在代表没有父母或没有孩子方面的表现与[Empty]一样好。 -
nub是如何定义的? -
@KaariLandry 该更改不会解决您遇到的任何问题。这只是一般的 Haskell 风格建议。这就是为什么我把它放在评论中而不是回答 - 它对你的问题没有帮助。
标签: haskell list-comprehension