我有一个空格或逗号分隔的表,有两列,每行代表两个单词的等价性。
A B
B C
B D
C E
F G
我想要的是一个表格,每行列出所有相互等效的单词。
A B C D E
F G
也就是说,如果两个单词出现在输入的同一行上,那么它们最终必须出现在输出的同一行中。
任何工具都可以。
答案1
在 python 中,以输入文件作为参数开始:
import sys
res = [] # list of lists
for line in open(sys.argv[1]):
try:
x, y = line.split() # split on space
except ValueError:
line = line.rstrip()
x, y = line.split(',') # retry with comma
for l in res:
if x in l:
if y not in l:
l.append(y)
break
else:
res.append([x, y])
for line in res:
print ' '.join(line)
测试if y not in l:
跳过两次添加相同的值,我不确定是否需要这样做,或者源是否存在此类异常。您可以省略测试并始终执行l.append(y)
。
该代码首先尝试按空格分割,然后重试逗号。这假设逗号分隔的行中没有空格(即不是A, B
)。
嵌套for
循环使用(据我所知)python 的一个特殊性:else
只有当循环通过穷举结束时才会执行for
,而不是通过 break 语句。这意味着如果x
未找到,则该对将作为新列表附加到res
。
答案2
理论
这个问题被称为划分一个集合进入等价类,输入文件列出两两等价。可以借助以下工具解决不相交集数据结构。
不太抽象的例子是,例如,给定同义词对,将单词划分为同义词组:
large big
big great
great vast
small little
little tiny
变成:
large big great vast
small little tiny
红宝石溶液
不相交集在 ruby 标准库中不可用,因此我使用 ruby 来模拟它Hash
(在其他地方称为“关联数组”、“字典”、“映射”)。
#!/usr/bin/env ruby
# new elements end up in "singleton subsets"
subsets = Hash.new { |subsets, element| subsets[element] = [element] }
ARGF.each do |line|
x, y = line.scan(/[^\s,]/)
# these two emulate disjoint-set's "find" operation
x_set = subsets[x]
y_set = subsets[y]
# and this loop implements disjoint-set's "union"
y_set.each do |element, _|
subsets[element] = x_set << element
end unless x_set == y_set
end
puts subsets.values.uniq.map{|set| set.join(" ")}
用法
这需要命令行上的文件名或标准输入上的数据:
$ ruby so-162730.rb input.txt
A B C D E
F G
$ ruby so-162730.rb < input.txt
A B C D E
F G
awk解决方案
也许更适合这个网站。
在这里,我使用了稍微不同的不相交集实现:每个子集由其元素之一(“领导者”)表示。这使得联合操作变慢,但使用 awk 的简单数据类型更容易实现。
{
union(find($1), find($2));
}
END {
format_subsets();
for(i in subsets)
print subsets[i];
}
function find(element) {
if (!leaders[element])
leaders[element] = element;
return leaders[element];
}
function union(leader_1, leader_2) {
for(i in leaders)
if (leaders[i] == leader_2)
leaders[i] = leader_1;
}
function format_subsets() {
for(element in leaders) {
leader = leaders[element]
subsets[leader] = (subset = subsets[leader]) ? (subset OFS element) : element;
}
}
用法
$ awk -f so-162730.awk < input.txt
A B C D E
F G
或者对于空格或逗号分隔的输入:
$ awk -f so-162730.awk -F '[[:space:]]+|,' input.txt
A B C D E
F G