[サンスクリットページ雑感集・技術情報]

ウルドゥー語のソート

Since 2005/9/19 Last Updated 2005/11/9



  1. 辞書順ソートは難しい
     語を辞書の順にソート(並べ替え、整列)するというのは、一見簡単なようでなかなか難しい。「そんなの、Excelのデータ(D)-並べ替え(S)で一発じゃん?」と思うかもしれない。たしかに、最近のアプリケーションでは、ExcelからDOSのSORTコマンドに至るまで、当たり前のように辞書順ソートをしてくれるようになったので、問題の複雑さがわかりにくくなっているが、これらに頼らず、プログラミング言語やマクロなどを用いて自前でソートをしようとすると、とたんに難しい問題に直面する。単に文字列の大小を比較して並べ替えたのでは、コード順のソートになってしまい、辞書順にはならないのだ。
     コード順と辞書順とはどう違うか、次の例を見てもらいたい。
    コード順辞書順
    きゃくきゃく
    きやくきやく
    きょくキヤク
    ぎゃくぎゃく
    ギャグ
    キヤクきょく
    ギャグ
     このように、コード順ではカタカナはひらがなよりも後になってしまうため、単にひらがなをカタカナにしただけで、「ん」よりも後になってしまう。また濁点のついた文字はつかない文字よりも後になってしまうため、「きょく」より後に「ぎゃく」が来てしまう。
     これを防いで辞書順にするためには、濁点の有無、大きい仮名(や・ゆ・よ…)と小さい仮名(ゃ・ゅ・ょ…)との区別、ひらがなとカタカナとの区別などをひとまず無視したソートを行い、その上で、濁点のあるものとないものとどちらを先にするか、などの優先順位をつけるようにしなければならない(ところで、上記の辞書順はExcelを用いたのだが、「きゃく」<「きやく」というのは一般の辞書で採用されているのだろうか。ふつう「きやく」<「きゃく」だと思うのだが)。
     この問題は日本語の文字が複雑だからではない。英語だって同様。たとえば英語では、ASCIIコード順でソートすると、大文字で書かれたものが前になってしまい、Zで始まる語がaで始まる語の前になってしまうという事態が生じる。だからまずは、大文字・小文字を無視したソートを行い、そのうえで、大文字のものと小文字のものとの優先順位をつけるようにしなければならない。
     あとは英語の場合、複合語など、「スペースで区切る」「ハイフンで区切る」「区切りなし」などのヴァリエーションが生じるが、これらの順序を正しく処理することも必要だろう。単にコード順でソートすると、記号のために思わぬ順序にソートされてしまう。まずは記号類を一切無視してソートし、そのうえで記号の有無による順位付けをすることだろう。また、最初の語のあとにもってくるという流儀もあろう。
     こんなふうに、辞書順ソートというのはなかなか大変なのである。Excelなどのアプリケーションは、けっこう複雑な処理をしているのだ。



  2. 辞書順ソートとはどんなソートか
     当サイトは「インドの言語を言語を勉強する人のための〜ページ」なので、最終目標はサンスクリットやウルドゥー語、ヒンディー語などの辞書順ソートの実現である。だが急がば回れ、まずはひらがな、カタカナの辞書順ソートのやり方を考えてみよう。
     上でちらっと書いたように、辞書順ソートとは2段階の手順をふむソートである。つまり……
    1. [文字の統一]言語的に同一のものとみなすもの文字を統一する
    2. [優先順位]その結果同一になってしまう文字列の間の優先順位をつける
     具体的に上記の例でいうと、
    辞書順1.文字の統一2.優先順位
    きゃくきやく1
    きやくきやく2
    キヤクきやく3
    ぎゃくきやく4
    ギャグきやく5
    きょくきよく1
    1
     まず、1.の「文字の統一」は簡単。カタカナはひらがなに、小さい仮名や濁点つきの仮名は普通の仮名にするだけの話である。
     問題は2.の「優先順位」。要は上記のような順位をつけられればよいわけである(私はやはり小さい仮名のほうが大きい仮名より前に来るというのは抵抗があるが、いまそれは置く)。
     上記のように、同じく「きやく」になってしまうデータだけを抽出して、それらの中で優先順位をつけようとすると、けっこう処理が複雑になってしまうし、時間がかかる。そこで、他のデータと同じになろうとなるまいと、ともかくすべてのデータについて、文字の種類に応じてたとえば次のような値を算出してしまう。これを「重み」と呼ぶことにしよう。
    1. =ひらがな小文字
    2. =カタカナ小文字
    3. =ひらがな大文字
    4. =カタカナ大文字
    5. =ひらがな濁点
    6. =カタカナ濁点
    7. =ひらがな半濁点
    8. =カタカナ半濁点
     すべてのデータの各文字についてこの「重み」を計算しておき、文字の統一で同じになってしまったデータは、重みの値でソートすればいいのである。もっと簡単にいえば、ひらがな統一文字列を第一キー、重みを第二キーとしてソートするというわけである。
     さらに、単に文字列を重みに変換したのでは、たとえば文字列が20文字あれば、重みの値も20文字になってしまう(下の表の「重み1」)。どうせ数字なのだから、すべて足しこんでしまおう。そうすれば桁数を節約できる(下の表の「重み2」)。
     もっとも、単に足したのでは、ひらがな大文字2文字の重みと、ひらがな濁点1文字のデータの重みとが同じになってしまう。下の表を見てもらえばわかるように、うまく順位付けできていない。でも要はひらがな統一文字列で同じになるものどうしの順番さえ確定すればいいのであり、同じになってしまう文字列の発生は実際にはありそうでなかなかないので、実用上はこれでも差し支えないと思う。
     気になるのであれば、各文字について重みの種類は8つなので、足しこんでいく際に、前の重みに8をかけながら足していくというような方法も考えられる。下の表の「重み3」がそれであるが、うまく順位付けがなされているのがわかるだろう。もっとも、かけていく値を大きくしてしまうと、このような工夫の意味がなくなってしまう。たとえばかけていく値を10にしてしまうと、ずばり重み1と全く変わらない数値になってしまい、工夫の意味がなくなってしまうので要注意。
     さらに、上で「ひらがな統一文字列を第一キー、重みを第二キーとしてソート」と書いたが、実際にはひらがな統一文字列と重みを連結してしまえばいい。重みの最大値がたとえば4桁でおさまるなら重みの数字を4桁に統一して文字列に変換してしまい、ひらがな統一文字列のあとにくっつけてしまう。念のためスペースを一個はさむなどしておけば十分である。これに基づいてソートすればいいわけである。
    辞書順ひらがな統一文字列重み1重み2重み3
    きゃくきやく2022130
    きやくきやく2226146
    キヤクきやく3339219
    ぎゃくきやく4026258
    ギャグきやく51511333
    きょくきよく2024130
    222



  3. ソート用の文字列を作成する
     上記のように、いくらコンピュータの文字コードがうまく設定されていても、それをそのままソートに用いるわけにはいかない。  Unicodeのデーヴァナーガリーのコードはけっこううまくできていて、そのままソートすればそれなりにヒンディー語やサンスクリットの順になってくれるが、細かいところで辞書順とは異なる(だいたい、ヒンディー語とサンスクリットではソートの仕方が違うのだ。その件は「サンスクリットのソート」を参照)。
     まして、Unicodeのアラビア文字のコード順は、ウルドゥー語の順序と異なるところがあるので、ウルドゥー語ではそもそもUnicodeでソートすることは不可能。
     さらに、サンスクリットやパーリ語ではローマ字を用いることが多いし、データベースソフトによってはUnicodeを用いることができず、ヒンディー語やウルドゥー語をもローマ字転写の形でデータ化したりすることも多い。このようなローマ字の順番でソートしても辞書順とはまるきり異なる結果になるのは自明である。
     そこで、ソートするときには、Unicodeやローマ字などで表現された語を、辞書順に並ぶようなコードに変換し、そのコードでソートをすることになる。具体的には、このページの最後にあるような数字列に変換する。この数字列はあくまでソート用の内部的なものであり、さらに別の形で参照することはない。できれば、Unicodeやローマ字などから自動的に変換できるような形にしたい。



  4. ウルドゥー語のソート
     さて、いよいよウルドゥー語のソートの話である。
     ウルドゥー語は通常、短母音表記をしないので、同じつづりになってしまう語がいろいろある。これらの間の順位付けをどうするかも問題になろうが、いろいろな辞書を見るとまちまちで、特に基準らしいものはありそうにない。そこで母音記号類は一切無視することとする。
     で、母音記号を除いた文字の順序は次のとおりである。
    1. 順序は、 である(左から右に読むこと)。 字母表などではが割愛されているのが普通だが、 このようにの間に入る。
    2. は、の前に出してしまう流儀、逆にの後に回す流儀、と区別しないで混ぜてしまう流儀と三様ある。 たとえば20th Century Dic.は「前」だが、同じところから出ているポケット版は「混ぜ」、Urdu-English Vocaburaryでは「後」である。当サイトの語彙集はとりあえず「前」にしてある。
    3. と混ぜる。『ウルドゥー語常用6000語』ではわけている(が後)が、ほとんどの辞書では混ぜている。当サイトの語彙集でも混ぜることとしよう。
    4. と混ぜる。これについてはまず例外はないようだ。
    5. スペースは無視する。



  5. ウルドゥー語ソートの実現
     いよいよウルドゥー語ソートの実現である。ここでは当サイトの語彙集で実際にやっている方法を述べることにする。
    1. 次のようにコードを定める。
      01 02 03 04 05 06 07 08 09 10 11 12 13
      14 15 16 17 18 19 20 21 22 23 24 25 26
         
      27 28 29 30 31 32 34 35 36 37 38    
    2. 33が欠番なのは、当初はを33としていたから。現実にはは文字列の末尾にしか来ないので、33として処理してもいいだろう。
    3. 重みについては非常に手をぬいたやり方をする。すなわち、以下述べるすべてのケースについて、文字列先頭からの位置と同じ重みをつける。たとえば、先頭から3文字目がであり、7文字目がであれば、3+7で10を重みデータとする。「重みの違いのみで同じ文字列になる」ケースは意外に少ないので、これだけいい加減なやり方をしても実用上は十分である。
    4. は32とし、上記のような重みを加算する。なお、語中ではと表記されるので、32とし、重みは加算しない。
    5. は36とし、上記のような重みを加算する。
    6. は38とし、上記のような重みを加算する。
    7. スペースは一切無視するが、重みだけ加算する。もっとも現実にはスペースの有無の違いのみの同じ文字列というのはまず存在しないので、重みを計算する必要すらないだろうが、一応重みをつけておく。
    8. 重みデータはコードの最後に4桁で付加する。4桁に満たない数字の場合は頭に0をつける。つまり重みが12になったら、0012という重みデータをコードの最後につけるわけである。なぜ4桁かというと、語彙集のソートの場合、上記の「手抜き重み」の最大値は2桁であり、3桁になることはありえない。だが、だからといって2桁で付加してしまうと、文字コードとまぎれる可能性がある。たとえば重みが12になったとして、コード末尾に12とつけてしまうと、上記のコードと同じになってしまい、思わぬところにデータが飛んでしまうことが考えられる。これをふせぐために、文字列本体と重みの間に00を挿入するというわけである。そこでわざわざ重みデータを4桁の整数に変換するというわけである。
     サンプルとして、で始まる語の先頭あたりからいくつかピックアップしてみる。当サイトの語彙集ではこのような値を計算して、これに基づいてソートしている。
    ソート用コード
    350104350004
    35010435380004
    3501043538110132050010
    3501320003
    3501320332010000
    3501380003

     ヒンディー語やサンスクリットのソートについては稿を改めて記す。




※ご意見、ご教示などは、に戻り、掲示板あるいはメールで賜るとありがたく思います。