平和的和平についての理論的論理

back


「便利」と「利便」、「平和」と「和平」、「理論」と「論理」、「学力」と「力学」、 「家出(いえで)」と「出家(しゅっけ)」、「男優(だんゆう)」と「優男(やさおとこ)」…。 日本語には漢字の順序を逆にすると別の語になるものがありますが、 こうした単語はいったいいくつあるのでしょうか? オンラインで入手できる電子的な辞書を使えば、 こうした単語を自動的に見つけることができます。

このページでは実際のやり方を解説していますが、 真の目的は「UNIX のテキスト処理ツールの使い方を紹介する」ことにあります。 結果をさっさと知りたい方は こちら (12kbytes, EUC-JP漢字コード) をどうぞ。

ここでは、複雑なプログラミングは扱っていません。 計算機を使ううえでプログラミングが重要なのはいうまでもないのですが、 あらゆる目的のためにプログラムを組むのは賢いやり方とはいえません。 本当に重要なのは「プログラミングしないでやりたいことをやる」ことだと思います。 最近ではたいていのテキスト処理が Perl で行われることが多いのですが、 いきなり Perl でなんでもやろうとするのはオシャレではありません。 ここでは UNIX のツールボックスアプローチにのっとって、 なるべく簡単なコマンドのみを組み合わせて目的を達成する方法を紹介します。


  1. まず最初に、すべての名詞およびサ変動詞が含まれている辞書を用意しましょう。 ここでは ChaSen に付属している IPA 品詞辞書 (ipadic) を使います。 名詞の情報はすべて Noun*.dic という 12個のファイルに含まれています。 このファイルはこんな中身になっています:
           (品詞 (名詞 サ変接続)) ((見出し語 (頽廃 4029)) (読み タイハイ) (発音 タイハイ) )
           (品詞 (名詞 サ変接続)) ((見出し語 (独走 2909)) (読み ドクソウ) (発音 ドクソー) )
           (品詞 (名詞 サ変接続)) ((見出し語 (暗転 3423)) (読み アンテン) (発音 アンテン) )
           ...
           
  2. 余計な品詞情報はいらないので、このファイルから実際の名詞を表している見出し語だけをぬきだしましょう。 本当は ChaSen の辞書は S式 (上にあるようなカッコが入りくんだ式のことです) で書かれているので、まともに扱おうとすれば Lisp などの S式を認識するプログラムが必要なのですが、 どのみちこれは 1行に 1単語ずつ書かれているわけですし、 めんどうくさいのでここでは awk を使います。 上の「頽廃」の部分は左から数えて 5番目のカラムですね (スペースが左に4回現れている)。 なので、こうすれば見出し語の部分がとりだせます。 すべてのファイルをいっぺんに扱うため、まず cat で連結してから awk に通しています:
           $ cat Noun*.dic | awk '{print $5}' 
           (頽廃
           (独走
           (暗転
           ...
           
    これで欲しい部分だけがとりだせました。 けれども先頭にカッコがついてしまって邪魔ですね。これを tr で削除しましょう:
           $ cat Noun*.dic | awk '{print $5}' | tr -d '('
           頽廃
           独走
           暗転
           ...
           
    これで IPA 辞書に入っているすべての名詞のリストを得ることができました。 このリストの中には「ひっくり返せばじつは別の単語」と、 そのもとの単語 (といってもどっちが元の単語だかわからないのですが) が 両方ふくまれているはずです。
  3. さて、いったいどうやったら各々の単語について、それをひっくり返したものが このリスト中にあるかどうか調べられるのでしょうか?

    じつは 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 に保存しておきます。
  4. つぎにこのファイルを rev したものを /tmp/egoh に吐きます。 ちなみにここで使う rev は2バイト文字に対応したものを使う必要があります。 ここで使うファイルはすべて EUC 漢字コードで書かれているので、 Linux の rev では環境変数 LC_CTYPEja_JP.eucJP に すれば OK でした:
           $ rev /tmp/hoge > /tmp/egoh
           
  5. ここまできたら、いよいよこの 2つのファイルをつなげて 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 に保存しておきます。
  6. さて、これで「ひっくり返せばじつは別の単語」語のリストが手に入ったわけですが、 このリストはまだたとえば「論理」と「理論」という語を両方ふくんでいます。 また、「日曜日」などの単語はたんに対称なだけで、これは 1度しか出ていません。 これらを削除するのはちょっとやっかいですよ。

    まず、得られたリスト /tmp/fafa をさらに rev したものをつくります:

           $ rev /tmp/fafa > /tmp/afaf
           
  7. つぎに /tmp/fafa/tmp/afafjoin します。 これはそのままではむずかしいので、まず 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 までの数列を得るといったことができます)

  8. ここまでくればあとひといきです。 今の出力をふたたび awk に通せば、まず「左右対称語」は削除できるでしょう。 2番目のカラムと3番目のカラムが同じ行はスキップする (= 2番目と3番目が異なる行のみを表示する) のです :
    $ join /tmp/nfafa /tmp/nafaf | awk '$2 != $3'

    ちなみにここで削除された対称語は次のものです (これは $2 != $3 の部分を $2 == $3 にすれば 得られます):

    R&R, SOS, TFT, いけい, うるう, かすか, こねこ, しるし,
    しんし, ちゃち, どんど, ねんね, ぽっぽ, みなみ, アリア, イガイ,
    イチイ, ウトウ, カジカ, カッカ, キツツキ, クック, クッククック,
    ゲンゲ, サルサ, タフタ, トマト, ドッド, ナズナ, マグマ, ミナミ,
    ルール, 一対一, 琴の琴, 時分時, 人非人, 西北西, 石灰石, 弾道弾,
    東南東, 東北東, 筒井筒, 日曜日, 馬車馬, 白飛白, 文語文
    (どうでもいいですが、なぜ「西南西」がないんでしょうか?)

    なお、ここではあくまで文字レベルしか考慮しておらず、 「しんぶんし」などの発音による対称語は考えていません (ChaSen 辞書の読み一覧を使えばこれも簡単にできるでしょうが、のちの課題ということで)。

  9. つぎにリスト中にある反転された語の片方を削除します。たとえば上の「頽廃 廃頽」と 「廃頽 頽廃」のどちらかを削除したいわけです。

    反転された語は 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}'
           狂躁
           頽廃
           ...
           
  10. ついにここまで来ました。これで目的の単語リストがとりだせたことになります:
           CG(GC), FM(MF), ..., かすかす(すかすか), ちまちま(まちまち),
           たんび(びんた), どんな(なんど), ..., アーム(ムーア), アド(ドア), アポ(ポア),
           愛敬(敬愛), 愛情(情愛), 悪性(性悪), 圧制(制圧), 安保(保安), ...
           遊歴(歴遊), 理論(論理), 離陸(陸離)
           

    単語は全部で 959個ありました。「平和」と「和平」とか、「便利」と「利便」のように 互いに似た意味のものもありますが、「明文」と「文明」みたいにぜんぜん意味が違うものも 結構ありますね。

    完全なリストは revwords.txt (12kbytes, EUC-JP漢字コード) に 置いてありますので、回文をつくって遊ぶなり、どうぞご自由にお使いください。

  11. 備考: IPA 辞書は完璧ではないので、このリストにあげた語が全部であるというつもりは ありません (が、かなりの部分はカバーしているはずです)。 別に SKK 辞書などを使ってみるという選択肢もあります。 興味をおもちの方は試してみるとよいでしょう (ただし SKK 辞書の場合は ipadic に比べて前処理がすこしだけ余計にかかるかもしれませんが)。

    また、いわゆるふつうの回文語 (「しんぶんし」など) も ここで挙げた方法を使えば簡単に取り出すことができます。 名詞の見出し語のかわりに読みを使えばいいんだから簡単ですね。


追記:

Google 検索をしたら、すでに同じことをやっていた方がいらっしゃいました。 ここではひっくりかえした熟語の意味的な類似度を調べておられます。

逆配列漢字熟語対の意味類似度表


Last Modified: Fri May 30 16:13:19 EDT 2003 (05/31, 05:13 JST)
Yusuke Shinyama