「便利」と「利便」、「平和」と「和平」、「理論」と「論理」、「学力」と「力学」、 「家出(いえで)」と「出家(しゅっけ)」、「男優(だんゆう)」と「優男(やさおとこ)」…。 日本語には漢字の順序を逆にすると別の語になるものがありますが、 こうした単語はいったいいくつあるのでしょうか? オンラインで入手できる電子的な辞書を使えば、 こうした単語を自動的に見つけることができます。
このページでは実際のやり方を解説していますが、 真の目的は「UNIX のテキスト処理ツールの使い方を紹介する」ことにあります。 結果をさっさと知りたい方は こちら (12kbytes, EUC-JP漢字コード) をどうぞ。
ここでは、複雑なプログラミングは扱っていません。 計算機を使ううえでプログラミングが重要なのはいうまでもないのですが、 あらゆる目的のためにプログラムを組むのは賢いやり方とはいえません。 本当に重要なのは「プログラミングしないでやりたいことをやる」ことだと思います。 最近ではたいていのテキスト処理が Perl で行われることが多いのですが、 いきなり Perl でなんでもやろうとするのはオシャレではありません。 ここでは UNIX のツールボックスアプローチにのっとって、 なるべく簡単なコマンドのみを組み合わせて目的を達成する方法を紹介します。
Noun*.dic
という 12個のファイルに含まれています。
このファイルはこんな中身になっています:
(品詞 (名詞 サ変接続)) ((見出し語 (頽廃 4029)) (読み タイハイ) (発音 タイハイ) ) (品詞 (名詞 サ変接続)) ((見出し語 (独走 2909)) (読み ドクソウ) (発音 ドクソー) ) (品詞 (名詞 サ変接続)) ((見出し語 (暗転 3423)) (読み アンテン) (発音 アンテン) ) ...
これで欲しい部分だけがとりだせました。 けれども先頭にカッコがついてしまって邪魔ですね。これを tr で削除しましょう:$ cat Noun*.dic | awk '{print $5}' (頽廃 (独走 (暗転 ...
これで IPA 辞書に入っているすべての名詞のリストを得ることができました。 このリストの中には「ひっくり返せばじつは別の単語」と、 そのもとの単語 (といってもどっちが元の単語だかわからないのですが) が 両方ふくまれているはずです。$ cat Noun*.dic | awk '{print $5}' | tr -d '(' 頽廃 独走 暗転 ...
じつは UNIX には各行の文字列を左右反転して出力する rev という
コマンドがあります。これはもともとはたぶんソートを末尾順でやったりするために
使われているのでしょうが、ここではこのコマンドを使って、
「リスト中のある行を rev したものと同じ行がリスト中に存在するかどうか」を
調べればよいでしょう。これはどうするかわかりますか? sort | uniq -c
を使うのです!
この組み合わせは定跡ともいえるパターンですから覚えておいて損はありません。
はじめに重複した要素を取り出したいリストを sort
に通します。
すると、同じ内容の行はソートによって連続した 2つの行に現れるはずです。
つぎにこれを uniq -c
に通します。
uniq
コマンドはただふつうに使うと連続する行を単純に削除してひとつにまとめますが、
-c
オプションをつけると「何行連続していたか」をカウントした数値を
行頭につけてくれるのです。この機能を使えば連続して現れた行を調べることができ、
頻度 2以上の語だけをとりだすことができそうです。
まず、元のリスト中に重複したものがないよう、念のため sort | uniq
したものを
つくっておきませう:
$ cat Noun*.dic | awk '{print $5}' | tr -d '(' | sort | uniq 、 . ・ 0 1 ...
あれま。一文字からなる単語が入っていますね。
一文字の単語はつねにそれ自身の鏡像になるので、つねに反転させた語が存在してしまうことになります。
これはまずいのでこいつは除きたいところです。これには awk の length
関数を使いましょう。
ここでできたものをいったん$ cat Noun*.dic | awk '5 <= length($5) {print $5}' | tr -d '(' | sort | uniq > /tmp/hoge $ less /tmp/hoge 頽廃 独走 暗転 ...
/tmp/hoge
に保存しておきます。
/tmp/egoh
に吐きます。
ちなみにここで使う rev は2バイト文字に対応したものを使う必要があります。
ここで使うファイルはすべて EUC 漢字コードで書かれているので、
Linux の rev では環境変数 LC_CTYPE
を ja_JP.eucJP
に
すれば OK でした:
$ rev /tmp/hoge > /tmp/egoh
sort | uniq -c
して、
頻度 2以上のやつのみを取り出してやりましょう。もしある単語を反転したものが
このリストに含まれているなら、その単語はこれらのリストをつなげたものの中に
合計してちょうど 2回現れているはずです。簡単ですね:
$ cat /tmp/hoge /tmp/egoh | sort | uniq -c | awk '2 <= $1 {print $2}' 煌煌 瓢瓢 ...
あれま。同じ文字が 2度つづく語がたくさん取れてしまいました。 これでは面白くないので、こいつはフィルタしましょう。 面倒くさいのでここでは最初の文字と次の文字が同じかどうかを判定してやれば十分でしょう:
ここまでの結果を$ cat /tmp/hoge /tmp/egoh | sort | uniq -c | awk '2 <= $1 {print $2}' | awk 'substr($1,1,2) != substr($2,3,2)' > /tmp/fafa 狂躁 頽廃 和平 論理 ...
/tmp/fafa
に保存しておきます。
まず、得られたリスト /tmp/fafa
をさらに rev したものをつくります:
$ rev /tmp/fafa > /tmp/afaf
/tmp/fafa
と /tmp/afaf
を join
します。
これはそのままではむずかしいので、まず cat -n
を使って両方に番号をふってやります。
こうすると出力されるファイルの各行は 1番目のカラムに行番号が入り、2番目と 3番目に
それぞれの語とそれを反転したものが入ります:
$ cat -n /tmp/fafa > /tmp/nfafa $ cat -n /tmp/afaf > /tmp/nafaf $ join /tmp/nfafa /tmp/nafaf 1 狂躁 躁狂 2 頽廃 廃頽 3 廃頽 頽廃 ...
cat
コマンドの -n オプションは連結したファイルの
各行に通し番号 (ようするに行番号) をふるもので、これも覚えておいて損はない機能のひとつです。
(面白い使い方としては、たとえば yes "" | cat -n | head -10
などとやって
1 から 10 までの数列を得るといったことができます)
$ join /tmp/nfafa /tmp/nafaf | awk '$2 != $3'
ちなみにここで削除された対称語は次のものです (これは $2 != $3
の部分を $2 == $3
にすれば
得られます):
R&R, SOS, TFT, いけい, うるう, かすか, こねこ, しるし,(どうでもいいですが、なぜ「西南西」がないんでしょうか?)
しんし, ちゃち, どんど, ねんね, ぽっぽ, みなみ, アリア, イガイ,
イチイ, ウトウ, カジカ, カッカ, キツツキ, クック, クッククック,
ゲンゲ, サルサ, タフタ, トマト, ドッド, ナズナ, マグマ, ミナミ,
ルール, 一対一, 琴の琴, 時分時, 人非人, 西北西, 石灰石, 弾道弾,
東南東, 東北東, 筒井筒, 日曜日, 馬車馬, 白飛白, 文語文
なお、ここではあくまで文字レベルしか考慮しておらず、 「しんぶんし」などの発音による対称語は考えていません (ChaSen 辞書の読み一覧を使えばこれも簡単にできるでしょうが、のちの課題ということで)。
反転された語は 2度出現してきているわけですから、2度目の出現を削除したいのですが、 上の例 (「頽廃」) をみると、1度目に左側のカラムで出てきた語は 2度目には右側のカラムで出てくる ということに気づきます (反転されてるから)。 なので、ここでは awk の連想配列を使って、「1度左側のカラムで出てきた語を記憶しておいて、 2度目に右側のカラムに出てきたらその語はスキップさせる」ようにすればいいわけです。 どちらの語が先に出てくるかはわかりませんが、どっちにせよ 2つある単語の片割れをどちらかひとつ削除すればよいので、順序は問題ではありません:
$ join /tmp/nfafa /tmp/nafaf | awk '($2 != $3) && (!x[$3]) {x[$2]=1}' $
あれ、何も出てこないぞ?
ああそうだ、print 入れるの忘れてた…。awk はアクションを省略すると
デフォルト動作として print をしてくれますが、
アクションに何かを記述するとそれはなくなってしまうので、自分で追加しないといけません。
どうせ他のカラムはいらないので、$2
のみを出力させましょう。
$ join /tmp/nfafa /tmp/nafaf | awk '($2 != $3) && (!x[$3]) {x[$2]=1; print $2}' 狂躁 頽廃 ...
CG(GC), FM(MF), ..., かすかす(すかすか), ちまちま(まちまち), たんび(びんた), どんな(なんど), ..., アーム(ムーア), アド(ドア), アポ(ポア), 愛敬(敬愛), 愛情(情愛), 悪性(性悪), 圧制(制圧), 安保(保安), ... 遊歴(歴遊), 理論(論理), 離陸(陸離)
単語は全部で 959個ありました。「平和」と「和平」とか、「便利」と「利便」のように 互いに似た意味のものもありますが、「明文」と「文明」みたいにぜんぜん意味が違うものも 結構ありますね。
完全なリストは revwords.txt (12kbytes, EUC-JP漢字コード) に 置いてありますので、回文をつくって遊ぶなり、どうぞご自由にお使いください。
また、いわゆるふつうの回文語 (「しんぶんし」など) も ここで挙げた方法を使えば簡単に取り出すことができます。 名詞の見出し語のかわりに読みを使えばいいんだから簡単ですね。