我有一个文件来表示图形中节点的邻接列表,作为我需要解析的文本文件。第一行是节点总数。第二行是node1,以及它连接到的节点列表(无向图)。例如
7
2 3 -1
1 3 4 5 7 -1
1 2 -1
2 6 -1
2 6 -1
4 5 -1
2 -1
第1行:图表共有7个节点 line2:Node1连接到Node2,Node3 line3:Node2连接到Node1,Node3,Node4,Node5,Node7。
-1是没用的。
这是我目前的ruby实现。我正试图找到一种方法来设置它
def parse_file(filename)
total_nodes = `wc -l "#{filename}"`.strip.split(' ')[0].to_i
node_hash = Hash.new
File.foreach(filename).with_index do |line, line_num|
# convert each line into an array
line = line.strip.split(" ")
# take out weird -1 at the end of txt file in each line
line = line[0...-1]
#puts "#{line_num}: #{line}"
# how come node_hash[Node.new(line_num)] = line does not work?
node_hash[Node.new(line_num)] = line
end
end
parse_file('test_data.txt')
我的节点类有一个adjacency_nodes数组,我可以将node2和node3压入它。例如:node1.adjancency_nodes<<节点2
class Node
attr_accessor :id, :state, :adjacent_nodes, :graph
def initialize(id)
@id = id
@adjacent_nodes = []
end
def to_s
"node #{@id}"
end
end
循环遍历此文本文件,创建新节点并将其存储在散列中以及推送其所有邻接节点的最简洁方法是什么?
答案 0 :(得分:2)
系统调用的使用是奇怪的;你真的不需要它来获得文件中的第一行。
第一行代表节点数。
后面的每一行代表给定节点的相邻节点。行n
代表node (n-1)
的节点。
所以你可以逐行:
def parse_file(path)
# start
f = File.open(path, 'r')
# get node count. Convert to integer
num_nodes = f.readline.to_i
# create your nodes
nodes = {}
1.upto(num_nodes) do |id|
node = Node.new(id)
nodes[id] = node
end
# join them and stuff
1.upto(num_nodes) do |id|
node = nodes[id]
# for each line, read it, strip it, then split it
tokens = f.readline.strip.split(" ")
tokens.each do |other_id|
other_id = other_id.to_i
break if other_id == -1
# grab the node object, using the ID as key
other_node = nodes[other_id]
node.adjacent_nodes << other_node
end
end
# done
f.close
end
答案 1 :(得分:2)
有人可能会利用ruby支持技术上无限的交叉嵌套对象:
class Node
attr_accessor :id, :adjacents
def initialize(id)
@id = id
@adjacents = []
end
def to_s
"<#Node #{@adjacents.map(&:id).inspect}>"
end
end
class Graph
attr_accessor :nodes
def initialize(count)
@nodes = (1..count).map(&Node.method(:new))
end
def to_s
"<#Graph nodes: {#{@nodes.map(&:to_s)}}>"
end
end
input = "7\n2 3 -1\n1 3 4 5 7 -1\n1 2 -1\n2 6 -1\n2 6 -1\n4 5 -1\n2 -1"
graph, *nodes = input.split($/)
count = graph.to_i
result =
nodes.
each.
with_index.
with_object(Graph.new(count)) do |(line, idx), graph|
graph.nodes[idx].adjacents |=
line.split.map(&:to_i).
select { |e| e >= 1 && e <= count }.
map { |e| graph.nodes[e - 1] }
end
现在您拥有无限嵌套图(您可以在任何节点上更深入地调用adjacents
以获得正确的结果。)
可以通过以下方式实现顶级图形结构:
puts result.to_s
#⇒ <#Graph nodes: {["<#Node [2, 3]>",
# "<#Node [1, 3, 4, 5, 7]>",
# "<#Node [1, 2]>",
# "<#Node [2, 6]>",
# "<#Node [2, 6]>",
# "<#Node [4, 5]>",
# "<#Node [2]>"]}>
答案 2 :(得分:0)
这有一个家庭作业问题的所有标志出错,但我会尽力帮助。
我的节点类有一个adjacency_nodes数组,我可以将node2和node3压入它。例如:node1.adjancency_nodes&lt;&lt;节点2
是否要将节点ID推送到阵列或节点引用本身?
# how come node_hash[Node.new(line_num)] = line does not work?
你是什么意思“它不起作用?”它不会在您的哈希中添加该行吗?
您正在构建一个散列,其中键是节点引用,值是相邻节点。您实际上并没有修改每个节点的adjacent_nodes
属性。那是你想做的吗?此外,如果您有两行引用相同的节点ID,例如2
,您要将该节点实例化两次,例如Node.new(2)
将被调用两次。那是你想要做的吗?
看看你写的内容,我注意到了一些事情:
String#strip
换行时,你正在使用String#chomp
。 total_nodes
变量设置为8(而不是7)。 -1
终结器有点奇怪,但您可以使用它来确定何时停止处理该行。我估计你的教授计划发送一些错误的输入,如2 3 4 -1 5
,看看你的代码是否破裂。在这种情况下,您的代码应该只考虑相邻的节点2,3和4。parse_file
方法返回什么?它可能不会返回您认为它正在返回的内容,因为它是目前编写的。 考虑到这一点,让我们做一些改变:
String#chomp
和String#to_i
。def parse_file(filename)
# open the file in read-only mode
file = File.new(filename, 'r')
# read the first line as an integer to determine the number of nodes
num_nodes = file.readline.chomp.to_i
# preallocate our nodes so we can store adjacent node references
nodes = Array.new(num_nodes) { |i| Node.new(i + 1) }
# read the remaining lines containing node definitions
file.each_line.with_index do |line, i|
# parse the adjacent node ids as integers
line.chomp.split(' ').map(&:to_i).each do |node_id|
# a sentinel node id of -1 means stop processing
break if node_id < 0
# TODO: What's supposed to happen when the node doesn't exist?
#raise "Unknown node ID: #{node_id}" if node_id == 0 || node_id > num_nodes
# add the node reference to the list of adjacent nodes
nodes[i].adjacent_nodes << nodes[node_id - 1]
end
end
nodes
end