philosodad
philosodad

Reputation: 1808

replacing 'eval' with a better solution

This method works, but it works very slowly. I think one of the problems might be the 'eval' statements in the add_by method.

Some explanation: every Node object has three relevant attributes, :x, :y and :neighbors. :x and :y are integers representing planar coordinates, :neighbors is an array, and nodes are stored in the @nodes array. The goal is to find, for each node k in @nodes, the nodes that are within some distance d of k and add them to the @neighbors array of k.

 def set_neighbors d
    def add_by dim, d
      dict = {}
      @nodes.each{|k| dict[k] = []}
      @nodes.each_index do |k|
        up = k+1
        down = k-1
        while up < @nodes.length and ((eval '@nodes[k].'+ dim) - (eval '@nodes[up].'+dim)).abs <= d
          dict[@nodes[k]].push(@nodes[up])
          up += 1
        end
        while down >= 0 and ((eval '@nodes[k].'+ dim) - (eval '@nodes[down].'+dim)).abs <= d
          dict[@nodes[k]].push(@nodes[down])
          down -= 1
        end
      end
      return dict
    end
    @nodes.sort_by{|k| k.x}
    exis = add_by('x', d)
    whys = add_by('y', d)
    @nodes.each do |k|
      neighbors = exis[k]&whys[k]
      k.neighbors = neighbors.select{|j| planar_distance(j,k) <= d}
    end
  end

My question is, how would you do this without either repeating the add_by routine for x and y or using eval?

Upvotes: 0

Views: 498

Answers (2)

Jakub Hampl
Jakub Hampl

Reputation: 40533

You can avoid eval by using @nodes[k].send dim.to_sym.

I'm not sure what your code is doing exactly, but maybe a few pointers:

def set_neighbors d
  @nodes.each do |node|
    node.neighbors = @nodes.select do |n| 
      (node.x - n.x).abs <= d && 
      (node.x - n.x).abs <= d &&
      planar_distance(n,node) <= d
    end - node
  end
end

Upvotes: 3

Mark Thomas
Mark Thomas

Reputation: 37517

How would I do it? I'd use the Neo4J graph database via the neo4j gem (source). If you're concerned about performance, this is optimized for graph distance calculations. Also the API is very nice.

That being said, you really don't need eval at all. You can call a calculated method name on an object by using send. So you can replace the above with @nodes[k].send(dim), @nodes[up].send(dim), etc.

Upvotes: 2

Related Questions