質問

I'm trying to build a few primitive objects to store a graph:

Vertex = Struct.new(:parent, :rank)
Graph = Struct.new(:vertexes, :edges)
Edge = Struct.new(:weight, :endpoints)

The idea here is that :endpoints (and :parent) are Vertexes, and, naturally, Graph stores lists of Vertexes and Edges. I tried to set this up, and I ran into trouble:

[47] pry(main)> t_graph = Graph.new()
=> #<struct Graph vertexes=nil, edges=nil>
[48] pry(main)> t_graph[:vertexes] = Array.new(Vertex.new())
TypeError: can't convert Vertex into Integer
from (pry):48:in `initialize'

On the other hand, this works:

[49] pry(main)> t_graph[:vertexes] = [Vertex.new(), Vertex.new()]
=> [#<struct Vertex parent=nil, rank=nil>,
 #<struct Vertex parent=nil, rank=nil>]

What's the efficient way to generate a mass of empty Vertexes in a Graph (or any such relation) for testing?

役に立ちましたか?

解決 3

One way to simply populate a list of Vertexes is to use .times, which seems kind of clumsy, but creates a list (I added an :id value):

t_verts = []
9.times do t_verts << Vertex.new end
[10] pry(main)> t_verts
=> [#<struct Vertex parent=nil, rank=nil, id=nil>,
 #<struct Vertex parent=nil, rank=nil, id=nil>,
 #<struct Vertex parent=nil, rank=nil, id=nil>,
 #<struct Vertex parent=nil, rank=nil, id=nil>,
 #<struct Vertex parent=nil, rank=nil, id=nil>,
 #<struct Vertex parent=nil, rank=nil, id=nil>,
 #<struct Vertex parent=nil, rank=nil, id=nil>,
 #<struct Vertex parent=nil, rank=nil, id=nil>,
 #<struct Vertex parent=nil, rank=nil, id=nil>]

This creates different objects.

[11] pry(main)> t_verts.each do |vert|
[11] pry(main)*   vert.id = rand  
[11] pry(main)* end  
=> [#<struct Vertex parent=nil, rank=nil, id=0.755245226082449>,
 #<struct Vertex parent=nil, rank=nil, id=0.0167377598961559>,
 #<struct Vertex parent=nil, rank=nil, id=0.759799975185007>,
 #<struct Vertex parent=nil, rank=nil, id=0.613897002692335>,
 #<struct Vertex parent=nil, rank=nil, id=0.0407847317079812>,
 #<struct Vertex parent=nil, rank=nil, id=0.549475744759297>,
 #<struct Vertex parent=nil, rank=nil, id=0.571876769381891>,
 #<struct Vertex parent=nil, rank=nil, id=0.270800829904956>,
 #<struct Vertex parent=nil, rank=nil, id=0.814754555208641>]

To add these en masse to a Graph, Simone's method can be used to add the list of Vertexes to the Graph:

def add_vertex(new_verts) # Adds or updates vertexes non-destructively
    new_verts.each do |new_vert|
      # first case, redundant insert
      if self.vertexes.include?(new_vert)
        warn "Vertex already exists in graph, no replacement done"
        # second case, update
      elsif not (update_vert = self.vertexes.select { |vert| vert.id == new_vert.id }).empty?
        self.vertexes[self.vertexes.index(update_vert)] = new_vert
        # third case, append new
      else
        self.vertexes ||= []
        self.vertexes << vertex
      end
    end
  end

It looks like I need to build a constructor method that builds the Graph from an adjacency list and weight list; I don't see a simple way to do this in one big step.

他のヒント

Array.new is not the standard way to initialize an array with a value. If you want to create an array with a Vertex instance, use

t_graph[:vertexes] = [Vertex.new]

As described in the documentation, Array.new takes either an array or a Fixnum size of the array.

new(size=0, obj=nil)
new(array)
new(size) {|index| block }

Returns a new array.

In the first form, if no arguments are sent, the new array will be empty. When a size and an optional obj are sent, an array is created with size copies of obj. Take notice that all elements will reference the same object obj.

The second form creates a copy of the array passed as a parameter (the array is generated by calling #to_ary on the parameter).

One more thing: you can omit parenthesis when there are no args.

t_graph[:vertexes] = [Edge.new, Edge.new]

instead of

t_graph[:vertexes] = [Edge.new(), Edge.new()]

Keep in mind you can add methods to Structs.

Graph = Struct.new(:vertexes, :edges) do
  def add_vertex(vertex)
    self.vertexes ||= []
    self.vertexes <<  vertex
  end
end

g = Graph.new
g.add_vertex(Vertex.new)

I disagree a bit with Array.new not being useful here. Find below my use case which solves the posters problem, even though this has been displayed but not well explained above:

  [31] pry(main)> Vertex = Struct.new(:parent, :rank)
  => Vertex
  [32] pry(main)> Graph = Struct.new(:vertexes, :edges)
  => Graph
  [33] pry(main)> Edge = Struct.new(:weight, :endpoints)
  => Edge
  [34] pry(main)> t_graph = Graph.new()
  => #<struct Graph vertexes=nil, edges=nil>
  [35] pry(main)> t_graph[:vertexes] = Array.new(2) { Vertex.new }
  => [#<struct Vertex parent=nil, rank=nil>, #<struct Vertex parent=nil, rank=nil>]
  [36] pry(main)> t_graph[:vertexes] = Array.new(2, Vertex.new )
  => [#<struct Vertex parent=nil, rank=nil>, #<struct Vertex parent=nil, rank=nil>]

As displayed on line [35] you can call Array.new supplying the number of elements you want in the array in this case 2 and what the elements should be in this case Vertex.new

James Edward Gray II did a talk on Avdi Grimm's podcast(no advert intent here) that clearly explains a very similar example

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top