特集 2 Java の変遷と互換性を探る 互換性が保証されたプログラム ) に対して る。 1.0 から 1.1 , 1.1 から 1.2 への間には , 数 タフェイス , あるいはメソッドの宣言時に 統一されたプログラミングインタフェイス 多くの保守リリースが行われている。たと 修飾子として (public とか abstruct などと同 を提供するものを指す。さらに , Java プラ えば , 1.1 以降は , 1.1.1 から 1.1.8 まで ( さら 様の位置に ) 指定することで , 該当クラス , ットホームは首尾一貫した互換性のある実 に 1.1.7 にかぎっては 1.1.7A , 1.1.7B があっ インタフェイス , あるいはメソッドでは浮 装を実現するために必要な要求事項を満た た ) の保守リリースが行われた。しかしこ 動小数データに拡張表現を用いないことを していなければならない。 Java2 は , 現時 れらはおもにバグフィックスであり , 基本 明示することができるようになった。した 点での最新世代の Java プラットホーム処理 的な機能の変更は行われていない。したが がって , 1.1 , 1.2 の変更点のほとんどが 系である。本稿では可能なかぎりこれらを って , 機能上の大きな変更は 1.1 と 1.2 のリ コア API を中心とするクラスライプラリの SMI の呼び名に従って使い分けるようにし リース時点で行われている。 機能強化と , それをサポートするコンポー ているが , 総称的に使う場合すべてまとめ Java の言語仕様は登場した時点からかな ネントの追加に当てられている。 て「 JDK 系」と表現する場合もある。 り完成されたものであったため , JDKI. 0 が これらの拡張がどれほど大がかりなもの リリースされて以来 , 明白な文法的の拡張 であるかは , 各バージョンの配布ファイル は 2 度行われただけである。もっとも大き のサイズを見ると明らかである。 Win32 版 な拡張が JDK 1.1 が登場した時点で行われ のファイルのサイズは , JDK 1.0.2 ( 1.0 台の TabIe 2 は時系列に登場した仕様 ( あるい た内部クラスの追加と , それに伴ういくつ 最終リリース , 以下同様 ) で約 3.7M バイト , かの仕様変更・・・・・・無名クラスの追加 , イン は処理系 ) を列挙しているが , それを系列 JDK 1.1.8 で約 8.6M バイト , SDK 1.2 Standa スタンス初期化子 , および無名配列の追加 ごとに分類すると Fig. 6 のようになる。 JDK rd Ed ⅲ on では約 20M バイトにも達する。も 系の本流は , , 版を除くと , 過去にメ などである。また SDKI. 2 の登場時点で , ちろんこれは圧縮された形での配布 ( 1.1 以 浮動小数点の取り扱いに対して , ある種の 降はインストーラ内蔵の実行形式 ) である ジャーなリリースが 3 回行われていて , そ ハードウェアがサポートしている拡張表現 し , なおかっドキュメントは別である。 API れぞれ JDKI. 0 , 1.1 , SDKI. 2 である。この の基本ドキュメントを含めた配布ファイル 系列の現在の最新版は Java 2 Standard Editi を利用して実装を行うオプションが与えら のサイズ ( 展開した形式 ) は , それぞれおよ onSDK1.2 ( 実際にはバグフィックスが行 れた。それに関して s c ゆというキーワー われたため , 原稿執筆時点では 1.2.2 ) であ ドが新たに追加され , これをクラス , イン そ 8M バイト , 27M バイト , そして SDKI. 2 Fig. 6 Java の系列とリリース時期 ( バグフィックスリリースは省略 ) JDKI. 0, 1.1 , SDKI. 2 JDK 1 .0 ( 96 / 1 ) ↓ JavaCard APl(96/10 ) JavaCard 2.0 API(98/10 ) JDK 1 . 1 ( 97 / 2 ) PersonalJava 1 .0 ( 97 / 9 ) ↓ PersonalJava 1 . 1 ( 98 / 7 ) EmbeddedJava 1 .0 ( 98 / 1 1 ) ↓ Java 2 Standa 「 d Edition(98/12 ) EmbeddedJava 1 . 1 ( 99 / 1 ) JavaCard 2.1 APl(99/11 ) PersonalJava 1 .2 ( 99 / 1 2 ) Java 2 Enterprise Edition ( 99 / 1 2 ) Java 2 Micro Edition ( 99 / 5 ) 特集 2 Java の変遷と互換性を探る 59
J a フ恤クラミングリファレンス 詳説解体新冒 式 (Expression) ④ 今回から具体的な式の構文について説明する。その第 1 回として , おおもとである一次子から取りあげていく。 第 35 回 キー Java における式の構文定義の階層におい て , 基礎となる階層が一次子である。 で " 基礎 " の意味は , Java の式がこれを元に 組み立てられているということだ。ただし Java 以外の言語の場合では , どのような式 であってもそれを分解していけば必ず最後 Fig. 1 一次子に関連する構文の形式定義 一次子 : 非配列生成一次子 配列生成式 非配列生成一次子 : リテラル this クラス名 . this ( 式 ) クラスインスタンス生成式 フィールドアクセス メソッド呼び出し 配列アクセス クラスインスタンス生成式 : new 型名 ( 引数並び opt ) クラス本体 opt には一次子にたどり着くといえるのだが Java の構文定義には少し特殊な部分があっ て , 必ずしも常に一次子に帰着するわけで はない。これについてはあとで捕捉したい。 こではまず一次子の定義を説明する。 Fig. 1 から明らかなように , 実際には一次 子は「非配列生成一次子」か「配列生成式」 のどちらかであって , 前者は「『配列生成式』 ではない一次子」という意味である。 そこで , 非配列生成一次子 (PrimaryNon New. ray ) から考えてみよう。これには次 の 8 通りの構文がある。すなわち , ・リテラル ・ this ・クラス名 . this ・クラスインスタンス生成式 ・フィールドアクセス # F 「 om 1 . 1 #From 1 . 1 #F 「 om 1 . 1 #F 「 om 1 . 1 次元式 : 次元 : [ 式 ] 次元冂 フィールドアクセス : 一次子 . 識別子 supe 「 . 識別子 supe 「 . 識別子 ( 引数並び 0Pt ) 一次子 . 識別子 ( 引数並び opt ) 名前 ( 引数並び opt ) メソッド呼び出し : クラス名 . supe 「 . 識別子 N 訓リテラル 文字列リテラル 文字リテラル 論理リテラル 浮動小数リテラル 整数リテラル リテラル : 非配列生成一次子一式 ] 名前 [ 式】 配列アクセス : クラス名 . supe 「 . 識別子 ( 引数並び opt ) # From 1 . 1 # From 1 . 1 一次子 . new 識別子 ( 引数並び opt ) クラス本体 opt 引数並び : 式 引数並び , 式 配列生成式 : new 基本型次元式並び次元 opt 配列初期化子 opt new クラスまたはインタフェイス型次元式並び次元 opt 配列初期化子 opt 次元式並び : 次元式 次元式並び次元式 #F 「 om 1 . 1 Java プログラミングリファレンス詳説 JDK 解体新書 109
[ 備考 ] C 言語で , ポインタと配列を混同してい る例は昔からよくあります。この道何年と いうべテランのプログラマですら平気でや らかしていることもあるので油断はできま せん。たとえば , List 16 のようなプログラ ムは片方は OK ですが , 片方は NG になりま す。意外なことにこの道何年ものべテラ ンでも答えられないことがありますが , 両 者の違いはわかりますか ? List16 のコメ ントでも示していますが , ポインタで定義 したほうは NG になる場合があります ( なら ない場合もあるので , やっかいなんです これは , "ABC" という文字列がリードオ ンリーな領域に配置され , そのポインタ値 を a に格納するようにコンパイルされる場 合に NG となるのです。リードオンリーな 領域とは , たとえば組み込みプログラムな ら ROM の領域だったり , プロテクトのしっ かりした OS の場合 , 読み込みは OK でも書 き込み処理を行おうとすると例外を発生す るような領域を意味します。 一方 , 配列で定義したほうは常に "DEF' を読み書き可能な領域に置きます。また , データ " DEF " を格納可能なサイズで配列を 用意していますし , 書き換えも可能です。 C 言語の参考書で注意しなくてはならな 文字列の定義と書き換え List vo 日 - ) ~ char btl ゆ char *a 言 *ABCE List 配列のサイズが足りない bt01 第 ' ユ ' 訌 / * * / 第 : 32 C MAGAZINE 2 側 0 4 ーー gtrcpy(a„*ABOØ)i 0 41 void E(Y いのは , 配列とポインタは違う概念のもの なのにあたかもすべて相互に置換可能で あるかのように錯覚させる記述をしている ものが少なくないことです。あるいは参考 書を書いた筆者自身が錯覚している場合も あって , そのまま読者も錯覚を引きずって しまう危険があるということです。 文字列を格納する配列の サイズが小さい 深刻度☆☆☆ ( 重度 ) [ 症状 ] わけのわからないバグに悩まされます。 また , 移植性や安定性に欠けるプログラム になります。 [ 原因 ] 文字列の扱い , および配列に関する勉強 不足が原因です。 [ 対策 / 予防 ] 文字列・配列の勉強をやり直すしかあり ません。 [ 例外 ] なし。 [ 備考 ] 文字列の扱いもまた C 言語でよく混乱の 元になります。 C 言語では「文字列型」とい う型などありません。文字列を扱う場合は , を「文字列に都合よく最適化された型だろ う」と自分の都合のいいように解釈すると , いとも簡単に落とし穴にハマります。 たとえば , List 17 のようなプログラムは 問題を抱えています。というのも , 配列 a は 4 バイトのサイズしか用意されていない のに , そこに 5 バイトのデータをセットしよ うとしているからです。文字列は "ABCD' だから 4 バイトだろうというのは大間違い で , 実際には文末コード ( つまり ' \ 0 ' ) もあ るので , 合計で 5 バイトぶん用意しないと まずいのです。 しかも , C 言語では配列のあふれが発生 しても , その場で問題が発覚するのではな く , 隣接する変数を破壊したり , 関数の戻 りアドレスを破壊しながら処理を続けます。 実際の障害が発覚するのはこれらの破壊さ れた領域にアクセスしたときなので , 症状 を見ただけではそれがどのような原因によ って引き起こされているか判断しにくいと いう問題もあります。 ほかのプログラム言語では配列の範囲外 のアクセスが発生すると例外で落としたり , あるいは配列自身を自動拡張することで対 応する例もありますが , あくまで「効率優 先。効率のためなら危険な目にあってもか まわない」というスタンスの C 言語では , プ ログラマ自身が覚悟しないことには , たか 一般に ch 型の配列を使用しますが , 間違った strlen の解釈 0 如 y ャ rd 叩 ( 00 れ e 恤 *inS): ~ eh *theAns 第日 00 ( gt ヨ ( S ) ) 、 ( L ヨ theAns)C 日は 0 ( e 町加 s リ て e 加てれ the 町 間違った sizeof の解釈 0 a * my ー gt て d 叩 ( 00 れ 0 恤てれ S ) LiSt LiSt これ 0 r 社れ 0 齟 3 盟日 00 ( 風 28f ( S ) ノ←間違いをこれでは「 00 れ 6 で * 」の 占有サイズしか取れない * た は f ( U もし ! ま theÅns)I@ rg ( 山 e 町 s リ て etu てれ theAns;
カリ 画像処理を極める M を大きくする , すなわちハッシュリスト を拡大することで , リストの走査をさらに 減らすことができますが , あまり大きくす ると使用メモリが多くなってしまいます し , 無意味に大きくしても使用されない部 分が増えるだけとなるので , バランスを考 えて増減する必要があると思います。 直方体 ( ボックス ) への分割 画像の RGB ヒストグラムを求めたあとは 分割処理を行っていきます。 RGB ヒストグ ラムを配列で求めた場合には , 配列を走査 していくことで画素のない部分を除去した り , 直方体を分割したりといった処理を実 現できますが , 単方向リストなどのリスト 構造などを用いた場合には別途アルゴリズ ムを工夫する必要があります。それはリス ト構造では配列の場合と違って任意の場所 のデータを即座に取り出すことができない こと , データが順序正しく並んでいる保証 がないことなどに起因します。そこで , のようなデータ構造を用いた場合の分割処 理を考えてみましよう。 まず , 前述のハッシュ法を用いて RGB ア ルゴリズムを求めましたが , このままのデ ータ構造では分割処理が不便なので , 配列 を用いたデータ構造に変換することにしま す。つまり , データ構造としては , 前述の List2 に示した画素値と画素数をセットにし たものを要素とする配列にします。変換を 行う時点では画像上の色数は求められてい るので , 必要最低限の配列だけを用意する ことができます。なお , 以降の説明ではこ のような形で表現された RGB ヒストグラ ムを「 RGB データ配列」と呼ぶことにします。 ヒストグラムを分割するには , 大きく分 けると , ・画素のない部分のカット ・もっとも長い辺を画素数が等しくなる ように分割 というふたつの処理があります。今回使用 したデータ構造に適用できるように各処理 の実現方法を考えていきましよう。 画素のない部分のカット メディアンカットアルゴリズムではまず RGB ヒストグラムから画素のない部分を除 去した直方体を求める必要があります。この 処理は RGB ヒストグラムの画素の存在する 部分の RGB の最小値最大値を求める処理と 考えることができます。この処理は RGB の 配列をすべて走査して RGB それぞれの最小 値最大値を求めることにより実現できます。 分割 RGB ヒストグラムの画素のない外側部分 を除去した直方体が求まると , 直方体のも っとも長い辺を画素数が同じになるところ で分割します。 RGB 配列に対してどのよう な処理を行えば分割処理が実現できるかを 考える前に分割の情報をどのように保持す るかを考える必要があります。本処理では , 分割の情報を List 6 に示すように RGB デー タ配列のどの要素からが直方体内の画素値 なのか , 直方体内の色数はいくつなのかを 記録することによって行うことにします。 また処理の効率化のために直方体内の画素 数の情報も持たせておくことにします。 このデータは求めたい色集合の色数の大 きさを持った配列でとります。このような 情報をポックス情報と呼ぶことにします。 ポックス情報の初期状態は全画素がひと つの直方体内に存在するというところから 始まります。分割を行うにしたがってポッ クス情報は逐次更新されていきます。 このようなデータの持ち方で本当にうま くいくのか疑問を持たれる方もいるかもし れませんが , 処理をうまくエ夫することに よってこのような情報で分割された直方体 の情報を表現できます。 さて , これらのデータ形式を用いて分割 処理を行うわけですが , 分割処理では直方 体のもっとも長い辺を分割します。もっと も長い辺は RGB のうち , 最大値最小値の差 がもっとも大きいものなので , 最大値と最 小値の差がもっとも大きい辺と定義できま す [ 注 1 ] 。もっとも長い辺が求まれば , 分割 TabIe 1 工フェクトプラグインで用いる PmacsA 曰関数 PMsGetCoIo 「 Onlmage 機 引 能画像上の任意の画素値を取得する CD 旧 * img int X. y Pix32 * col 画像データ 指定する座標 画素値が設定される 数 typedef union { unsigned long color; st 「 uct { unsigned char b; unsigned char g. unsigned char 「 : unsigned char opa; }citem : }Pix32; / * カラー値 * / / * 透明度 * / 戻り値座標が画像内なら T 日 U 巳画像外なら FALSE PMsSetColo 「 Onlmage 機 引 能 数 画像上の任意の画素値を設定する CD 旧 * img 画像データ GRAM32* out 結果をセットする領域 戻り値 int x,y Pix32 *col int reg int flag なし 指定する座標 セットする画素値 領域情報 ( 通常は与えられたものをそのまま渡せばよい ) 座標の種類を表す。工フェクタの場合は FALSE でよい 画像処理を極めるアルゴリズムラボ 121
特集 2 Java の変遷と互換性を探る 理を行うことでプログラムの見通しがよく 雑すぎる機能はあっさりと放棄した。たと いないかどうかが常に検査されるし ( C / C + + えば C + + の多重継承 , テンプレート , 演算 なるのであるが , C / C + + といった言語でマ では配列の添え字の範囲検査はいっさい行 ルチスレッドプログラミングを行うには OS われない ) , オプジェクトをダウンキャスト 子オーバロードなどはサポートしていない。 あるいはシステムライプラリの助けを借り そうした機能をサポートしなくても表現能 ( クラスの階層で下層 ( サプクラス ) 方向へ なければならず , スレッド間の制御につい 力が低くはならないという判断からである。 のキャスト ) する際には , 実際にキャスト ても複雑な手順が必要になりがちである。 また Java では , プログラマのケアレスミ 対象のオプジェクトが妥当なクラスのイン それに対して Java ではきわめて簡単にマル スによるシステムへの致命的な悪影響を回 スタンスであるかどうかが自動的に検査さ チスレッドが利用できるため , 気軽にそれ 避するような言語仕様を採用しているし , れる。 を実現できる。 コンパイラは可能な範囲で , コンパイル時 こうした言語仕様上の配慮によって , Ja にソースコードの誤りを検出しようとして va で記述したプログラムは , 同じ注意を払 C / C + + との親和性と相違 いる。さらに実行時ルーチンがプログラマ って作成した C / C + + プログラムよりも信頼 をサポートするとともに操作に関する正 性が向上するとされている。 Java の言語仕様のうち , とくに構文を中 当性のチェックを入れるようになっている。 ini に代表される 心とした表面的な部分には , C / C + + との強 たとえば言語仕様上では , C / C + + でプログ Java の応用技術 い類似性が与えられている。これは意図的 ラマが間違いを犯す最大の原因といわれて なもので , C / C + + のプログラマが Java へ移行 いるポインタが排除されている。またコン する際の障害をなるべく低くしようという パイラには , 初期化されていない変数の検 SMI は 1999 年 1 月に Jini (gee-nee と発音す 配慮からである。しかしながら , Java と C/ 出や , 到達不能なコードの存在を検出する る ) と称する新しい技術を発表した。 Jini は C + + とはいくつかの点で正反対の設計思想 ことが義務付けられていて , これによって Java と対立するものではなく , Java を用い を採用しており , それは必然的に言語仕様 もプログラマの意図しない誤りを早期に発 て , その上に構築された新しいアーキテク にも反映されている。たとえば C / C + + では , 見できるようになっている。 チャである。 Java という名前がとくに何か 言語仕様を詳細に決めて処理速度を遅くす そして実行時ルーティンの一部としてガ の略ではなかったのと同様に Ji ⅲという るよりは , 特定のハードウェア上での実行 べージコレクションを実装することで , プ 名前は J ⅲ i ls Not lnitial であるといわれてい 速度が最高にできるように細かい部分の ログラマが直接低レベルなメモリ管理を行 て , とくに何かの頭文字とか省略形ではな 実装方法を各処理系の裁量に任せるという わずに済むようにしている。 C / C + + では , アプローチをとっている。逆に Java は移植 ( ヒープから ) メモリを割り付けて使用した Jini を利用することで , さまざまなサー 性を第 1 に優先させ , 言語仕様上に各処理 あと , その領域が不要になったならば , free ビスとクライアントが , ネットワークに対 して簡単かつ自由に , 接続したり切り離し 系ごとで異なる解釈が生じる余地を排除し や delete を用いてプログラマが明示的に領 ている。たとえば , Java では long, int, sho たりできるようになる。 Jini の用語に従え 域を解放する処理が必要であった。しかし れの 3 種類の整数型のビットサイズは CPU の Java では , 不要な領域はガべージコレクシ ば , Jini 技術を採用したプログラム間では 種類によらず , 常に 64 ビット , 32 ビット , ョンによって自動的に回収されるため , 自発的 (spontaneous) なやりとりが行われ , うした明示的な解放処理は不要である。プ その結果さまざまな機器やソフトウェアに 16 ビットである。 C / C + + では処理系の都合 で整数のビットサイズを変えてよいので , ログラマがメモリの解放に気を配らなけれ よってひとつの , 動的な分散システムが構 違う環境へプログラムを移植した場合に思 ばならないと , 解放するべき領域の解放し 成され , それを連邦 (federation) と呼んでい わぬ不都合が生じることがある経験者も多 忘れや , 逆に解放してしまったメモリ領域 こで , クライアントとは , ビデオデ る。 を参照するなど , ありがちで , かっ発見が いだろう。 ッキやテレビ , オーディオ , さらには冷蔵 そして Java では , C + + の膨大で複雑怪奇 困難なバグの原因になることが知られてい 庫や電子レンジ , あるいは留守番電話や な言語仕様への批判と反省から , 極力シン る。このような問題については , Java のよ FAX などの家電製品でもよいし , レーザプ プルな言語仕様を目指した。 C + + では似た うにシステムが自動的に管理することによ リンタやイメージスキャナなどのようなコ ような働きをする言語機能が複数存在して りプログラムの信頼性は大きく向上すると ンヒュータベリフェラルでもよい。またサ いるのだが , そういった冗長な機能は完全 いわれている。 ービスとは , それぞれのハードウェアが ( ソ フトウェアの助けを借りて ) , ほかの機器 に排除され , ある働きを実現するための機 さらに Java では , 配列をアクセスする際 に提供する機能を指す。たとえばレーザプ 能はひとつだけに限定されている。また複 の添え字の値が許容される範囲を逸脱して 夢 特集 2 Java の変遷と互換性を探る 55
List pe 「でコマンドラインから入力された引数を表示する が , 正規表現のメタ文字として使われてい す。ひとつはシフト JIS コードを使わずに る記号が検索文字列に含まれているときに 日本語 EUC コードを使うことです。日本 は注意が必要です。たとえば C 言語の配列 語 EUC の文字コードは最上位ビットが立 (a [i] のようなもの ) を検索するため " [ " を っているので , 1 バイトの英記号で使われ 含む行を表示しようとしたとします。 ている範囲と重なることがありません。 でたとえば , もうひとつの方法は日本語対応になって print grep(/[/, く FILE>) ; いる JPerI を使うことです。 JPerI では日本 # 誤り のようにすると , プログラムは , 語の文字を 1 文字として認識するので 2 ノヾ / [ / : unmatched [ ] in regexp . イト目がほかの文字と誤解されてしまうと というエラーメッセージを出して終了して いう心配がなくなります。 しまいます。このエラーメッセージは「正 ・コマンドラインの引数 規表現 ( regexp ) のなかで " [ ] " が対応して いません」という意味です。実は " [ " と " ] " 検索する文字列をプログラムのなかに埋 は正規表現のなかで文字クラスというもの め込んでしまうと , 検索文字列を変更する を表現するためのメタ文字で , 必ず " [ " と " ] " 必要が生じたときにいちいちプログラムを と書くことができます。配列@ARGV を数 が対になっていなくてはなりません ( 文字 修正しなくてはなりません。これを避ける 値比較である = = 演算子で使っていますね。 クラスというのは , 正規表現のなかで " [ a ー には , コマンドラインから検索文字列を入 このとき , 配列@ARGV は要素の個数を返 z ] " で英小文字 1 文字を表したり , " [ 13579 ] " 力することです。コマンドラインで perl ス すことになります。 C 言語ではコマンドラ で奇数の数字 1 文字を表したりするための クリプトに与えられた引数は , 特殊な配列 , インの引数は , もの ) 。 " [ " のメタ文字としての効果をな / * C 言語 * / @ARGV くすためには前に " \ " を置いて次のように に格納されています ( ARGV の部分は大文 main (int argc, char * argv[] ) します。 字 ) 。これは引数列 (argument vector) を表 の argc と ar のふたつの仮引数で知ること print grep(M/, く FILE>] ; しています。個々の引数は順に # 正しい ができました。 Perl では配列@ARGV だけ たとえば , [i] という文字列を探すには $ARGV [ 0 ] で知ることができます。ちなみに Java 言語 次のように書きます。 $ARGV[I ] の配列も自分の長さを知っているので , print grep(/%[i%]/, <FILE>) ; # 正しい $ARGV[2] 三五 * / / * Java= ⅱロ 個々の記号の前に \ を置く代わりに全体 $ARGV[3] main (String [ ] args) を }Q—YE でくくることもできます。 }Q— の args というひとつの仮引数でコマンドラ \ E の範囲にあるメタ文字は自動的に \ が付 としてアクセスできます。何個の引数が与 インからの引数を知ることができます。配 えられたかは , 別の特殊な変数 $ ARGC で いたのと同じように扱われます。 列の要素数は args. len h で得られます。 与えられま・・・・・・いいえ , 違います。 perl の print grep(/YQ[i]YE/, <FILE>) ; # 正しい コマンドラインから入力された引数を表 配列は自分の大きさを知っています。 perl 示するプログラムを上記の 3 種類の言語で ・日本語を扱ううえでの注意点 の配列をスカラコンテキストで評価すれ 書いてみましよう ( List4 ~ 6 ) 。比較してみ 日本語を扱う場合には注意が必要です。 ば , 配列の要素数を得ることができます。 てください。ただし , 0 番目の引数の意味 2 バイト文字のなかに検索する文字がたま つまり , 引数がないときの if 文は , は C 言語だけ異なります。 C 言語だけ 0 番目 たま含まれてしまう場合があるからです。 if (@ARGV = = 0 ) { の引数はコマンド名になるからです。 Perl たとえば Windows で使われている文字コ die ”引数がありません¥ n ” で $ ARGV [ 0 ] はコマンドではなく引数です。 ード ( シフト JIS) では , 「データ」や「コンピ List Java でコマンドラインから入力された引数を表示する ュータ」で使われている「一」の 2 バイト目 が [ と同じ値になります ( 16 進数で 5B ) 。つ まり , / \ [ / を使って検索すると , 「データ」 や「コンピュータ」にマッチしてしまうこ とになるのです。 これを避ける方法は大きくふたつありま 92 C MAGAZINE 2000 4 1 foreach $arg (@ARGV) { print ” $arg%nn; 2 List c 言語でコマンドラインから ) 功された引数を表示する 1 : #include <stdio. h> 2 : 3 : void main(int argc, char * a て [ J) int 新 5 : 6 : 7 : 8 : for ( 土 = i く argc; i 十十 ) { printf("%s%n", argv[i] 1 : class Test { public static void main(String[ ] args) { 2 : f0 て (int 土 = i く args.length; 土十十 ) { 3 : System. out. println(args[i] 4 : 5 : 6 :
用できるような処理方法を考える必要があ ります。 以下 , 前述のふたつの処理についてそれ ぞれ考えていきましよう。 日 GB ヒストグラム メディアンカットアルゴリズムを実現す るためには , RGB ヒストグラムを求める必 要があります。この RGB ヒストグラムをど のようなデータ形式で表現するかが実装面 においては重要になってきます。もっとも 簡単な方法は RGB それぞれを添え字とする 三次元配列で表現する方法です。つまり , 画素値 (), G , B) の画素数を RGBhist[R] CG] [ B ] のような配列に格納する方法です。 このデータ構造で RGB ヒストグラムを求め るためには List 1 のようなプログラムを書 けばよいでしよう。しかし , この方法では 冒頭で述べたとおり RGB ヒストグラムのた めに大量のメモリを必要とします。配列を long 型 ( 32 ビット ) でとった場合には 64M バ イト , short 型 ( 16 ビット ) でとったとしても List List 2 を単方向リストにする 32M ノヾイトが必要になります。画素数が 256 の範囲を超えることは十分考えらるので , 8 ビット型は実用上利用できません ( 利用で きたとしても 16M バイトのメモリが必要で す ) 。より少ないメモリで RGB ヒストグラ ムを求めるにはどうすればよいでしようか。 日 GB 値と画素数をセットにする 省メモリ化を図るために利用できる事象 として , 1600 万色の色を扱うことができる フルカラーでは , 画像上にこれらすべての 色が利用されることは稀であるということ があります。たとえば , 800X600 の大きさ の画像では , 画素数は 48 万画素なので画像 内で 48 万色以上の色が使用されることは物 理的にありえません。また一般的な画像で は全画素の色が異なることは少なく , 使用 される色の分布にはかなりの偏りがある場 合がほとんどなので , 画像内で使用されて いる色数はもっと少ないはずです。そこで RGB 値と画素数をセットにして格納する方 法が考えられます。 このようなデータを格納するためには , まれ Fig. 2 リスト構造 チェーン状にデータをつないでいく 次のデータへのポインタで List 2 のようなデータ構造にします。この ようなデータを画像上に存在する色数だけ 用意して , 画素値と画素数をベアにして記 録しておきます。ある画素値の画素数を得 るには , このデータを調べて画素値が一致 するものの画素数を求めることで行います。 もし , データ内に一致する画素値が存在し なければ , その画素値は画像上に存在しな いとみなされます。 単方向リストを用いる こでこのようなデータをいくつ用意す ればよいかが新たな問題となります。色数 が事前にわかっている場合にはその色数ぶ んの配列を用意すればよいのですが , ヒス トグラムを求める , もしくはそれに近い処 理を行わないと色数は求まりません。この ようにデータ数が可変となる場合には一般 に Fig. 2 のようなデータ構造を用います。 この構造は単方向リストと呼ばれるもの で , 必要に応じて鎖状に伸ばしていくこと により , 可変長のデータ個数を持つデータ を扱うことができます。このようなデータ 終端には NULL をセット NULL typedef struct LIST { int r,%b; int num; struct LIST *next ・ } List; ハッシュ法 ハッシュ 関数 検索キー / / RGB 値 / / 画素数 / / ポインタチェー ( ハッシュ値 ) インデックス 検索キーにハッシュ関数を適用し ハッシュ値を求め . それをインデックスとして 配列内を検索する 1 18 C MAGAZINE 2 刈 4 データ値 ZZZ ァータ配列 0 インテックス シノニムに対応したデータ構造 インデックスデータ値 0 データ配列 用いる タを格納できるように単方向リストを 同じインデックスの場所に複数のテー
〇〇〇〇 C の標準規格の改訂版 RevisedC 血 はここでは触れない ・「 _pragma(arg) 」演算子は pragma 命令とよく似た振る舞い をするが , それ単独で 1 行に書く必要がなく , その引数は マクロ展開の対象となる。したがって , プラグマのように 振る舞うマクロを書くことができる。これは C89 にはない 機能だ 言語へのそのほかの追加としては以下のようなものがある。 ・「「 estrict 」型修飾子は , ポインタに適用された場合にはコン パイラに対して「そのポインタが指す配列がほかの左辺値 からアクセス可能な別の配列と重なっていない」ことを再 保証する。複雑に聞こえるかもしれないが , 実際そういう ことだ。その目的は , べクトル化命令を持つプロセッサに おいてそれを使った最適化を促進するというものだ。関数 内の制限付きポインタ引数は重複する配列のオペランドを 指すことはないはずなので , オプティマイザは予想外の副 作用について心配する必要はない ・そのサイズが実行時に計算される式であるような「可変長 の配列」を宣言できる。配列のサイズを実引数から得るこ とを示すのに , 「 a [ * ] 」のように関数の引数を書ける。ま た , アスタリスクの部分により詳しい宣言を書くこともで きるが , その規則はとても複雑でここで説明できない。基 本的概念としては , 標準の C はいまや C89 に比べてずっと 柔軟な配列の表記が可能で , Fo 市 an によく似ているという ことだ ・新しい整数型「 long long int 」および「 unsigned long long int 」 は少なくとも有効な 64 のビットを持つ ・新しい型「一 B00 凵は標準 c + + のブール型「 b00 凵とよく似た性 質のものだ ・新しい型修飾子「一 Comp 厄 x 」は , 3 種類の浮動小数型のそれ ぞれに適用して , 3 種類のデカルト複素数算術型を指定す ることができる。型修飾子「」 maginary 」を付加して , 同様 に三つの純虚数算術型の組を定義できるような実装も可能 だが , 必須ではない ・整数の除算は確実に 0 に近付く方向への切り捨てを行うよ うになった ( C89 では負の商を切り捨てるのに 2 種類の方法 が可能だった ) ・「 inline 」キーワードは , 標準 C + + と同様にコンパイラが関数 のためのインラインコードを生成するのを促進する ・コンパイラ機能は「 func. ー」という名前を各関数にローカ ルな静的な配列オブジェクトとしてあらかじめ定義する。 これには , その関数名がヌル終端文字列として格納される ライプラリの細かな変更点 C99 は既存のライプラリのヘッダに数多くの小さな変更を必 要とする。 それらのうちいくつかには , く stdio. h > ヘッダ ( 伝統的な 1 バイ ト版 ) および <wchar. h> ヘッダ (Amendment 1 で追加されたワイ ド文字版 ) に宣言されている入出力関数への変更が伴う。 ・「 fprintf 」系の関数には変換記述子「 % a 」「 % A 」が加わった。 これは 16 進法の浮動小数点数の出力を指定するものだ。 れらの変換記述子を「 fscanf 」系の関数に使うこともできる が , それらはいずれも入力として 16 進数の浮動小数点数を 受け入れられるようになった ・浮動小数点数の無限大は「 inf 」または「 infinity 」として表示さ れ , 「 not-a-number(NaN) 」は「 nan 」として表示されるよう になった。大文字の変換記述子を使うことで , これらを大 文字にするることができる。同様に , 入力の浮動小数点変 換でもこれらの形式が受け付けられる ・く stdio. h > ヘッダには「 snprin 廿」関数が追加された。この関数 はサイズ制限付きの文字バッフアにテキストを生成する。 これに相当するワイド文字版の関数はすでに Amendment 1 で追加されている ・「 vp ⅱ n 廿」系の関数に似た「 vscanf 」系の関数が , 1 バイト版お よびワイド文字版の両方について追加された ・いくつかの実装ではバイナリストリームの先頭で「 ungetc 」 を呼ぶことができるが , このような慣習は避けるべきであ ると決まった ライプラリへのそのほかの小さな変更点を以下にあげる。 ・く float. h > ヘッダにはふたつのマクロが追加された。たとえ ばマクロ「 DEC AL 一団 GIT 」は表現可能な浮動小数点数の 精度をすべて保持するのに 10 進数で何桁必要かを示すもの だ ( これは「 LDBL 一団 G 」と同じである必要はない。こちらは やや小さめであることが多い ) ・ <math. h> ヘッダではすべての数学関数が浮動小数点数の 3 種類の精度に対して提供されることが必要となった。 C89 では「 floa 凵版および「 long doub 」版の名前を定義している が , それらを要求してはいない。標準 C + + では C99 と同様 に 3 種類全部を必要とする ・ <math. h> ヘッダは多くの新しい数学関数の全体を , 浮動小 数点数の 3 種類の精度すべてにおいて定義する。これらは C89 で割愛されていた POS Ⅸや旧 EE 754 などの標準規格で 特別記事 C の標準規格の改訂版 19
23 0 したときに画素数が同じになる部分を求め っとも色数の多いものを対象に分割処 割は行えないので , 処理対象とはせず , 次 ますがここで , 同じ直方体内に存在する 理を行う に画素数の多いものを対象とします。 ③直方体の数が求めたい色数と同じにな 部分の RGB 配列は分割を行う色成分の値で すべての直方体の色数が 1 以下の場合 , 直 並べ替えられているとします。このように るまで②を繰り返す 方体内の色数がすべて 1 にまで分割された することで , RGB 配列の先頭から画素値の 分割対象となる直方体は直方体情報をす ということであり , これ以上の分割を行え 累積数を求めることで分割点を求めること べて走査して , もっとも画素数の多いもの ません ( このような状態になるのは画像上 ができます。 を選択することで実現できますにこでポ の色数が求めたい色集合の数よりも小さい さて , これらをまとめると直方体分割を ックス情報を色数でソートして先頭のもの 場合であり , 減色の必要がそもそもない場 実現する処理概要は , 次のような流れとな を選ぶ , という方法も考えられますが , 画 合ですが ) 。この場合は分割処理をこの時 ります。 素数最大のものを求めるだけなので , ソー 点で終了させます。 ①直方体内にすべての色が入っている状 トまで行う必要はないでしよう ) 。また , 最 RGB 配列で表現されたデータに対して分 大の画素数を持つ直方体であっても内部に 態からスタートする 割処理を行うためには , 次のような手順が ②現在 , 分割された直方体のなかからも 存在する色数が 1 以下の場合 , これ以上分 考えられます。 ①分割したい色成分のみをソートキーと してソートを行う Fig. 5 メディアンカット法による減色結果 ②ソートされた配列の頭から画素数を合 (a) 元画像 算していき , 直方体内の総画素数の半 数もしくは半数を超えた地点で分割を 行う 具体的に説明しますと , ①ではたとえば R の軸で分割を行いたい場合には R 値のみ を見てソートを行います。これは R 軸で分 割を行う際には GB 値の分布状況はあまり 考慮する必要がないためです。ソートが完 了した段階では , RGB 配列はソートキーと なった色に対する濃度ヒストグラムと同じ ような状態となります。ですから , これを 画素数が等しくなるように分割するために は先頭から画素数を足していき , 画素数が 総画素数の半分を超えたところで分割して やれば実現できます。 このように直方体内をソートしていくこ とにより , 一度分割された要素は 1 か所に かたまって存在することになり , 次の分割 などによってほかの直方体に飛び出すこと はありえません。ですから , ポックス情報 は直方体の始まる箇所と色数だけを保持し ておけばよいことになります。 [ 注 1 ] これにより RGB に重み付けをすることで R 成分を多く 残す , などの制御を行うことができます。人間の目には R > G > B の順に色の変化に敏感に感じられますのでこのよう な制御を行うことでより自然な減色を行うこともできます。 結果 List7. c( 付録 CD-ROM に収録 ) に解説した (c) 16 階調 (b) 8 階調 Fig. 6 減色による画像の劣化 (a) 減色前 (b) 減色後 122 C MAGAZINE 20 4
ロの List ハッシュを使う ( out ⅱ ne2. pl) List 同じ動作をする謎のプログラム ( out ⅱ ne3. pl) $file = yourfile. txt' 1 : 2 : 3 : 4 : 5 : 6 : 8 : open(FILE, $file) 0 て die; 9 : while ( く FILE>) { foreach $regex (keys %prefix) { 10 : if ( /$regex/) ( 11 : print $prefix{$regex) , $ 12 : 13 : last ー 14 : 15 : 16 : ) 17 : close(FlLE); $file = yourfile. txt' 1 : 2 : 8prefix = ( 3 : ' ◆ 5 : 6 : . join( ' ) Ⅱ ' , keys(%prefix)) 8 : $pattern = ( ( ' 9 : open(FILE, $file) or die; 10 : while (<FILE>) { print($prefix{$ 十 ), (-) if ( /*$pattern/); 11 : 12 : ) 13 : close(FILE); ハッシュの添え字のことを「キー」と呼び , 短く書けばよい , というものではないです ・シンプルに それに対応する要素を「値」と呼びます。 ね。 List 14(outline0. (l) は正しく動作するプ List 16(outline2. (l) では , ハッシュ %pre 同じ動作をするもっとよいプログラムが ログラムですが , 特殊な変数 $ ーを使うと f ⅸのキーは「 ^ ◆」のような文字列で値は「」 ありましたら , ぜひ結城浩くhyuki@hyuki.c もっとシンプルに書くことができます。 Li のような空白文字の列です。ハッシュ % om > までご連絡ください。 st 15 ( outl ⅲ el. (l) はいちいち $ ⅱ ne に代入せ prefix を初期化するときにはコンマ区切り 次回は「ネットワーキング」 ず , 暗黙のうちに仮定される変数 $ ーを活 の代わりに = > が使え , 対応関係をわかり 用しています。これだとムダな記述がない やすく書くことができます。またこれだと , ぶん , どういうパターンにマッチさせてい インデントを空白ではなく「 > 」にしたり 今回はテキスト処理のいくつかの例を示 るかが読みやすくなりますが , その反面 「→」にしたりするときでも修正が楽になり しました。いかがでしたか。次回は「ネッ Perl に慣れていない人には何をやっている ます。 トワーキングの楽しみ」と題して , Perl で List 16 (outline2. (l) では foreach を使って かわかりにくいと思われる危険性もありま ネットワークプログラミングを行ってみま しよう。どうぞご期待ください。 す。 ハッシュ %prefix のキ—$regex に関するル ープを作り , 現在対象になっている行 $ ー もしも , ご意見やご質問がありましたら , ・ハッシュ ( 連想配列 ) を使う が $ regex にマッチするかどうかを調べて 本誌綴じ込みの編集部へのハガキでお知ら せくだされば幸いです。また , ご遠慮なく List 15(outline1. (l) は慣れてくればわか います。マッチした場合には , $prefixl$r egex Ⅱすなわちその正規表現に対応した 結城浩くhyuki@hyuki.com/ へメールをお送 りやすいプログラムですが , 記号とインデ りください。本連載に関するメールには表 ントの関係をさらにはっきりと表すために インデント ) を現在行の前に置いて表示し 書き換えてみましよう。 perl のハッシュ 題に [MP] という文字を含めてくださると ます。 last というのは C 言語の break に似て ( 連想配列 ) と呼ばれるデータ構造を使いま こでは foreach を中断する 助かります。本連載に関する U 糺は , いるもので , す。 のに使います。 http://www hyuki.com/mp/ List 16 (outline2. (l) は , 「 ^ ◆」のような正 foreach をなくして List 17 (outline3. (l) の です。 規表現と , インデントの対応を % pre ⅱ x と ようなプログラムを書くこともできます。 参考文献等 いうハッシュで表現したものです。 これも不思議なことに List 16(outIine2. pl) ハッシュというデータ構造は , ひとこと と同じ動作をします。 [ 1 ] 『 Effective Perl 』 , Joseph N. HaII , RandaI でいえば「文字列が添え字になっているよ 変数 $ pa れ ern は「 ^ ( ( ◎月 ( ・月 ( ◆月 ( ◎ ) ) 」 L. Schwarts 著 , 吉川邦夫訳 , アスキー うな配列」です。通常の配列は , 数字が添 という正規表現になり , これとマッチさせ 出版局 ると特殊な変数 $ + に「最後にマッチしたカ え字になっており , 配列@array の 0 番目の http://www effectiveperl.com/ 要素は $ array [ 0 ] で表されます。一方ハッ ッコの内容」が格納されます。そのため , [ 2 ] 『詳説正規表現』 , Jeffrey E. F. Friedl 著 , $prefix 月で目的のインデントが取り出 シュは文字列が添え字の役目を果たし , ハ 歌代和正監訳 , オライリー・ジャパン ッシュ %hash の 'key' という文字列に対応 [ 3 ] メールマガジン「 perl クイズ』 せるのです。 こまでやると読みにくく感 する要素は $ hash ド key Ⅱで表されます。 じる人は多くなるでしよう。プログラムは http://www.hyuki.com/pq/ Pe 日プログラミングの楽しみ 97