2
data Node = Node Char [Node] deriving(Eq)

nodeA = Node 'A' [nodeB, nodeC, nodeD]
nodeB = Node 'B' []
nodeC = Node 'C' [nodeE, nodeF]
nodeD = Node 'D' []
nodeE = Node 'E' [nodeB]
nodeF = Node 'F' [nodeA]

deepTraverse :: Node -> [Char]
deepTraverse (Node c []) = [c]
deepTraverse (Node c ns) = c:(map (\(Node cl nsl)->cl) (buildNodeList (Node c ns) ns))
             where lssToLs lss = foldr (++) [] lss
                   buildNodeList nw nsw = lssToLs (map (\(Node cl nsl)->(if (Node cl nsl) == nw then [(Node cl nsl)]
                                                                         else ((Node cl nsl):(buildNodeList nw nsl)))) nsw)

main :: IO()
main = putStrLn (show (deepTraverse nodeA))

Whenever I call deepTraverse nodeA it loops gets hung up. In ghci it does spit out:

Main*> "ABCEBF

Leading me to suspect the "then" part of the if. I have been banging my head against this problem for a while, any help would be appreciated.

James
  • 153
  • 3
  • 12
  • 1
    Tip: `lssToLs` is identical to the standard `concat` function. Also, there's a function called `concatMap`. None of which has anything to do with your bug; it's just a passing comment. – MathematicalOrchid Oct 27 '15 at 21:56
  • Thank you, I tested the fixed version as alluded to in my comments on the answer given by @MathematicalOrchid with concat, and it does indeed work the same. Forgot to check the prelude functions for that one. – James Oct 27 '15 at 22:08
  • See also: [graph representation](http://stackoverflow.com/questions/9732084/how-do-you-represent-a-graph-in-haskell), [detecting sharing](http://stackoverflow.com/questions/19355772/is-it-ever-possible-to-detect-sharing-in-haskell), [equality-testing on infinite structures](http://stackoverflow.com/questions/28243314/is-equality-testing-possible-between-two-infinite-data-structure-in-haskell). – Daniel Wagner Oct 28 '15 at 00:18

1 Answers1

3

After staring at your code for ages trying to figure out what it's supposed to do and why it's getting stuck, I think I realise the problem.

nodeF points back to nodeA. (I only just realised that!) It seems you're trying to use the == operator to figure out when you arrive back at a node you already looked that. That won't work.

When you say node1 == node2, the == operator will traverse the entire tree to see whether the two values are equal. If they are equal, and this structure contains an infinite loop... well, then your code loops forever! Try it. If you ask nodeA == nodeA, I think you'll find it never returns. There's your bug.

The only way to fix this is to put a genuinely unique label on every Node, and compare only the labels. You can't check whether two variables "point to" the same thing in Haskell; you can only compare whether two structures have the same value, which means traversing them completely.

MathematicalOrchid
  • 58,942
  • 16
  • 110
  • 206
  • I thought that it would test the data type's constructor to see if they are equal. But then I suppose that means that it will expand the node list's elements constructors. Hence the loop. I think I will test if the Char parts of node are equal. Also sorry I did say in the question that the data type represents a directed graph. And that the deep traverse is designed to move through the graph and backtrack when it reaches a dead end, stopping when it gets back to the starting node. – James Oct 27 '15 at 22:04
  • If the character labels are supposed to be unique then yes, this is the correct solution. – MathematicalOrchid Oct 27 '15 at 22:05
  • Yes, I have notice this issue. The work is for University, so I shall have to ask my lecturer if this is the case. Although, as the question states the Char value in the node declarations names I would imagine this to be the case. – James Oct 27 '15 at 22:11
  • @James "I thought it [`(==)`] would test the data type's constructor to see if they are equal." But your data type only has one constructor, hence any two values have the same constructor; so I don't believe that's what you thought! – Daniel Wagner Oct 28 '15 at 00:36
  • @DanielWagner I meant the constructor for the constant, not the type. e.g. `Node 'A' [nodeB, nodeC, nodeD]` not `Node Char [Node]` – James Oct 28 '15 at 00:47
  • @James Okay, then it seems to be a terminology issue. "Constructor" specifically means only the "tag" part of the expression -- e.g. `Node` is a constructor (for things of type `Node`), `'A'` is a constructor (for things of type `Char`, `[]` and `(:)` are constructors (for things of type `[a]` for some `a`), and so on. A thing like `Node 'A' [nodeB, nodeC, nodeD]` would instead be called a "term" or an "expression"; in some cases you might also hear it called a "value". Also, you're right that it will inspect the whole value -- and that is the problem, since the value is an infinite one. – Daniel Wagner Oct 28 '15 at 00:54