スペースまたはコンマで区切られた2つの列を持つテーブルがあり、各行は2つの単語の同等性を表します。
A B
B C
B D
C E
F G
私が欲しいのは、各行が相互に同等の単語をすべてリストしたテーブルです。
A B C D E
F G
つまり、入力の同じ行に2つの単語が出現した場合、それらは出力の同じ行に含まれる必要があります。
どんなツールでもかまいません。
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:
_は、同じ値を2回追加することをスキップします。それが必要かどうか、またはソースにそのような異常があるかどうかはわかりません。テストを省略して、常にl.append(y)
を実行できます。
コードは最初にスペースで分割を試み、次にコンマを再試行します。これは、コンマで区切られた行にスペースがないことを前提としています(つまり、_A, B
_ではありません)。
ネストされたfor
ループは(AFAIK)を使用しますpython特殊性:else
は、for
ループが枯渇によって終了した場合にのみ実行されます。これは、x
が見つからない場合、ペアが新しいリストとしてres
に追加されることを意味します。
この問題は 集合の分割 から 同値類 として知られており、入力ファイルにはペアごとの同値がリストされています。 disjoint-set データ構造の助けを借りて解決することができます。
あまり抽象的な例は、例えばです。同義語のペアを指定して、単語を同義語のグループに分割します。
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(" ")}
これは、コマンドラインのファイル名またはstdinのデータを想定しています。
$ 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
おそらくこのサイトにより適しています。
ここでは、disjoint-setのわずかに異なる実装を使用します。各サブセットは、その要素の1つ(「リーダー」)によって表されます。これにより、和集合の操作が遅くなりますが、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