In article <e0b1e5700908042053g2ffecc1dg4ee7df9493f6c480 / mail.gmail.com>,
  Yusuke ENDOH <mame / tsg.ne.jp> writes:

> The following program implements a graph with adjacency list by
> using Array and Struct:
>
>   Node = Struct.new(:item, :neighbors)
>   node1 = Node.new(1, [])
>   node2 = Node.new(2, [node1])
>   node1.neighbors << node2

If node2's item is 1, eql? cannot distinguish node1 and
node2 on Ruby 1.9.2.

So, DFS doesn't work well on 1.9.2.

% ./ruby -v -e '
Node = Struct.new(:item, :neighbors)
node1 = Node.new(1, [])
node2 = Node.new(1, [node1])
node1.neighbors << node2
p node1.eql?(node2)
'
ruby 1.9.2dev (2009-08-05 trunk 24405) [i686-linux]
true

> This function worked on 1.8.7 and 1.9.1p243, but not on trunk.

node1.eql?(node2) should be false for DFS.
The program works because it is false on 1.8.7 and 1.9.1.

The program doesn't work reliably on 1.9.2 because eql? is
changed it to return true.  You can notice it with the
exception.

You can use Hash#compare_by_identity to accomplish it on 1.9.

% ./ruby -ve '
Node = Struct.new(:item, :neighbors)
node1 = Node.new(1, [])
node2 = Node.new(1, [node1])
node1.neighbors << node2

def dfs(node, visited = {}, &block)
  return if visited[node]
  yield(node)
  visited[node] = true
  node.neighbors.each {|n| dfs(n, visited, &block) }
end

visited = {}
visited.compare_by_identity

dfs(node1, visited) {|n| p n.item }
'
ruby 1.9.2dev (2009-07-28 trunk 24300) [i686-linux]
1
1

> If hash of recursive structure were a constant, this function
> would work.  Collisions, however, would be terribly frequent.

With Hash#compare_by_identity, DFS works well without
terrible performace problem.

If hashes of node1 and node2 is constant, it doesn't work as
follows.

% ./ruby -ve '
Node = Struct.new(:item, :neighbors)
node1 = Node.new(1, [])
node2 = Node.new(1, [node1])
node1.neighbors << node2

def node1.hash() 0 end
def node2.hash() 0 end

def dfs(node, visited = {}, &block)
  return if visited[node]
  yield(node)
  visited[node] = true
  node.neighbors.each {|n| dfs(n, visited, &block) }
end

dfs(node1) {|n| p n.item }
'
ruby 1.9.2dev (2009-08-05 trunk 24405) [i686-linux]
1

> I wonder that it might be intrinsically unreasonable to use
> primitive data types for such a purpose.

Hash can be used, with compare_by_identity.
-- 
Tanaka Akira