TOP > プログラマ2.0日報 > 2008年04月

あすなろBlogger

facebookに投稿 このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク このエントリーをはてなブックマークに追加 この記事をクリップ! livedoorclip ユーザー数 BuzzurlにブックマークBuzzurlにブックマーク この記事をtweetする

プログラミング文章読本~比喩は的確に

2008.04.29

小説家というと、「比喩」の考案に命を削る...というようなイメージがあります(古いかな?)が、プログラムでもコメントを付ける時には、「適切な比喩」をしたいよね...とは思います。

え、プログラミングで「比喩」って?

はい、それはデザインパターンです。

デザインパターンは元々、

頻繁に使われる解決策に名前をつけて、それらを『プログラマ同士の間』でもコミュニケーションの手段として役立つようにしよう!

という意図もあってああいうカタログができたわけです。言い換えると「知ってる」だけじゃ意味がなく、使って、しかも「使ってる」ことを言葉で名指すことが、「デザインパターンを使う」ことなのです。ですから、プログラムでパターンを使っていたら、これはぜひぜひソースの先頭にでもパターン名とそのクラスの役割名を書いておくべきです。

あるいは、

/** Factory Method */
public abstract MyInterface getMyInterface();

のように、パターンで特徴的なメソッドにその旨を書いておき、「これを上書きして適切な継承クラスを返すサブクラスを作るんだよ!」というあたりを明示すべきでしょう。この時、いちいち説明を書くよりも、ただ一言「Factory Method」の方が断然優れています。「饒舌で詳細な記述よりも、適切な比喩」というあたりでしょうか? 比喩の元は、プログラムのような抽象構造の場合には、デザインパターンそのものなのです。

で...これを少し発展させると、こういう考え方もできます。

@Immutable
public final class ImmutableClass {
.....
}

このクラスは Immutable なものとします。その時、アノテーションをコメントと兼用して、「このクラスは Immutable である」ことを伝えると同時に、この @Immutable アノテーションを処理して、「このクラスが本当に Immutable の制約条件にしたがっているか」をチェックする、というのはアリでしょう。 Immutable の場合は、ざっとこれくらいが制約条件になります。

  1. 1. final クラスである(望ましい)
  2. 2. フィールドはすべて final 修飾がなされている(望ましい)。
  3. 3. セッタは存在せず、すべてコンストラクタ引数で値がセットされる。
  4. 4. List などの「集合」を返すメソッドではコピーしたものを返す。

このような形式的条件だったら、ある程度自動的にチェックすることも可能です。でしたら、アノテーションプロセッサを自分で作ってしまうのもアリでしょう。

「プログラマに向けられた注釈」と「自動チェック可能な制約」とを同時に兼ねるようなコメントのあり方....というのは実はかなり強力な武器になる可能性があります。実際、こういう「プログラマ向けの注釈&その内容を検証する自動処理」のコンセプトをEiffel のような「契約によるプログラミング」言語は備えています。単に「プログラマが読んで役に立つ」から一歩進んで、「それと同時に、そこで書かれた制約条件を、自動的にチェックできる」というのは、なんと面白くしかもパワフルなことでしょう!

投稿者 : 杉浦 こずえ | 投稿日時 : 2008.04.29 21:11

facebookに投稿 このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク このエントリーをはてなブックマークに追加 この記事をクリップ! livedoorclip ユーザー数 BuzzurlにブックマークBuzzurlにブックマーク この記事をtweetする

プログラミング文章読本~何よりメリハリ

2008.04.26

「あすなろBlog昇格記念」の新シリーズです。その名も「プログラミング文章読本」と題しましょう。

昔の名文を謳われた小説家がよく「文章読本」というタイトルで、「文章をどう書くか?」というエッセイを書いています。で....プログラムっていうのも、やっぱり「文章」なんですよね! ですから、「(あくまで)文章」という視点で、プログラムを考察するという企画モノです。プログラムは「動く」のと同時に、ソースの内容を「読む人へ伝達する」という重要な機能を持っています。ですから、「プログラムとしての機能性」以外にも、「読みやすさ」というのはとても重要な「性質」なんですよね。

ですから、「読みやすく、理解しやすいプログラムのためのTips」みたいな感じかな。私は結構その点で「過激!」かもしれませんけど、実際コレかなり重要なことだと思います。

じゃあ、本題。今日のテーマは「メリハリ」です。

小説だと、重要な場面の微に入り細に入り、精密な描写をするものですが、重要でない場面だと「『省筆』をうまく使って、読者の想像力を活用する」という重要なテクニックがあります。逆に悪い文章は、「何が言いたいのか、パッと見でよく判らない...」ということにもなります。

プログラムも同じですね。けど、プログラムの方が分が悪いです....確かに長いメソッドは「何か重要そうだ」と思わせますし、短いメソッドは「あまり重要ではなさそうだ」と思わせます。また、コメントの多い・少ないも「一見して判る重要性」を区別する手がかりになります。しかし、「ここ重要!」「ここはコンベンショナルに定義されているだけ(のたとえば JavaBeans のゲッタ・セッタ)」というあたりの区別に無関心なケースが極めて多いです.....

ゲッタ・セッタしかない部分を、フルコメントでずらずらと記述してあって、それがエディタ上で5ページ以上もあると、結構うんざりしますよね。要するにこれ、「メリハリ」という観点で改善できないか?とは思いませんか?

私がよく使う記述ルールはですね、こういうモノです。

重要ではない部分の記述は、出来るだけ改行をサボる

というものです。例えば、ゲッタ・セッタが並ぶのならば、

public String getString() { return string; }
public void setString(Srting s) { string = s; }
public int getIntVar() { return intVar; }
public void setIntVar(int s) { intVar = s; }
.......

という感じにしてしまいます。普通はこういうの、改行を入れますし、引数も何となくそれっぽい名前を使うのですが、これは

定義されていれば、特に実装に関心がないです

というのを積極的に示すために、あえてこういう書き方で書いてしまいます。ここで、たとえば、引数のポリモルフィズムを使う同名メソッドがあったとしましょう。

public String getString() { return string; }
public void setString(Srting s) { string = s; }
/** setString() は int も受ける */
public void setString(int val) {
  string = String.valueOf(val);
}
public int getIntVar() { return intVar; }
public void setIntVar(int s) { intVar = s; }
.......

ね、setString(int) だけ、他のメソッド定義と書き方が違います。ですから、これは目立ちますし、わざわざこれを定義した甲斐もある、というものです。また、javadoc にした時も、「コメントが入ってる」という違いがあるので目立つわけです。

同じようなことで、私が使う「わざと改行しない」書法というと、「メソッド内で事前条件を示すのに、改行しない」という書き方(ガード、さすがに仕事だと表明は使いづらい....)があります。

/**
 * @param x 負でない
 * @param y 負でない
 */
public int sub( int x, int y ) {
  if( x < 0 )   return -1;
  if( y < 0 )   return -1;
  ....実際の処理
  if( z < 0 ) {
    return 0;
  ....実際の処理
  return z;
  }

これ、たとえば x, y が負の数だと処理自体に意味がないメソッドだとしましょう(Math.log() みたいなイメージかな)。このメソッドを呼んで、

もしx,yに負の数を与えて、何か問題が起きたとすれば、その責任は sub() というメソッドの側にあるというよりも、読んだ側の問題である

と言えるようなコンテキストがある場合(javadoc コメントでこの事前条件を公開もしています)、実際の処理の中で起きる「処理の終了によるリターン」と区別して、メソッドの先頭で「改行{}のないぶらさがりifで書く」ことで、これを明示します。

結構今でも、「return が一箇所にしかない=構造化プログラミング」と信じちゃってるプログラマがいるみたいですが、たとえば、

public void sub( int x, int y ) {
  int ret = -1;
  if( x >= 0 ) {
    if( y >= 0 ) {
      ....実際の処理
     if( z >= 0 ) {
      ret = z;
     } else {
      ret = 0;
     }
    }
  }
  return ret;
}

なんていう、途中return を回避するために if のインデントがやたらと深くなったコード(これCOBOL?)なんかよりも、ずっと「意味深く」ありませんか?まあ、今ならば return を色分けして目立たせるエディタを使っていない....わけはないでしょうから、「途中の return を見失う」なんて可能性はほとんどないわけです。特にこういう if の連鎖でインデントが深くなったプログラム、というのは、「メリハリが全然なくて、ダラダラと書かれたプログラム」の典型じゃないか、とも思います....

さて、今回過激でしたね。これ結構私のオリジナルの工夫が多いのですけども、「読みやすさを向上させる」という点で気に入ってます。どうでしょう?

 

投稿者 : 杉浦 こずえ | 投稿日時 : 2008.04.26 09:23

facebookに投稿 このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク このエントリーをはてなブックマークに追加 この記事をクリップ! livedoorclip ユーザー数 BuzzurlにブックマークBuzzurlにブックマーク この記事をtweetする

知られざる Iterator

2008.04.23

さて、ちょっとデザパタ話。Iterator なんて極めてありふれたデザパタで、そんなんよく判ってる.....はずですが、実はそうでもない、という話です。

今書いているプログラムで、私が書いたのを参考に、別なプログラムを書く若い人が、「Iterator を返すメソッド、なんて実装するのは初めて見ました...」なんて言ってきたのです。まあ、若い子だから許してね! 要するに、List を返すインターフェイスには慣れていても、Iterator を直に返すようなインターフェイスには初めてお目にかかったらしいです。

私、結構 Iterator を返すのが好きだったりします。自前で内部無名クラスで Iterator を生成して返す...という奴ですね。実際、List を返すのとは区別して使っています。それは、

  1. 1. Iterator のソースとなる(Aggregate な)クラス内に保持された、一度作ったら変わらない(のがアタリマエな) List を返すのならば、List である。
  2. 2. Aggregate で構築される、複雑なデータ(たとえば木構造のような)を、「順に」参照するために、一時的に生成する List ならば、Iterator で返す。

という感じでしょうか。感覚的には、

Iterator で返すのは、Aggregate が保持する実体の「スナップショット」である

というところです。「順次アクセス」をするために一時的に作られる List を Iterator で覆って返す、という感じかな。

Iterator というのは、実は「脆弱な」部分を抱えたデザパタだったりします。たとえば、

Iterator ite = aggre.iterator();
while( ite.hasNext() ) {
   Object o = ite.next();
   // 何か副作用のある呼び出しをする
   aggre.someOperationWithSideEffect();  
}

という格好のコードの場合、副作用のある Aggregate のメソッドを呼ぶことで、この Iterator が内部で参照する(実際の)List オブジェクトが変更されて、Iterator は参照をラップしているだけなので、その「副作用」の影響が出てしまう...ということがありえます。言い換えると、

そういう副作用のある呼び出しを、Iterator のループ内でしてはならない

という暗黙のルールがあるわけです。しかし、これは「Iterator の作り方」によっては影響がないかたちにできないわけではないです。いわゆる「スナップショットIterator」のやり方を取れば、

Aggregate のターゲットとなるオブジェクトが、Iterator 生成時にコピーされ、Iterator の内部に保持されて、Iterator は Aggregate のオブジェクトではなく、自身の内部で抱えるオブジェクトを参照する

というかたちになります。この場合、Aggregate のオブジェクトの変更は、Iterator には本質的には影響しないことになります(間接的に変わることはありえますが...)。

勿論、「スナップショットIterator」の場合、オブジェクトのコピーという「高価」な処理をするために、一般にオススメな技法というわけではありません。スナップショットを使うのは、たとえば「マルチスレッド環境での平行性を高める」というあたりのメリットを狙って使うことが多いでしょう。実際、マルチスレッド環境では、参照型の Iterator の場合は、

Iterator ite = aggre.iterator();
synchronized( aggre ) {
   while( ite.hasNext() ) {
     Object o = ite.next();
     // 何かする
   }
}

くらいの感じで保護することが多いでしょう。これはコンテキストによっては、while ループ参照のあいだ中、ロックされ続けて時間を浪費するかもしれません....もし、スナップショットで事が足りるのならば、Iterator をスナップショットにして synchronized を外すことが出来るかもしれません。

マルチスレッドを考慮しなくてもいい場合でも、木構造を Aggregate が保持するために、一旦「再帰を使うなどして順次に収集」しないことには順次アクセスができないタイプのデータならば、実際その収集作業はスナップショットのような格好を取ります。スナップショットである一時的な List は、下手に内部で保持すべきものではありませんから、もし、Iterator でないにせよ、そのまま外部に渡すべきでしょう。でしたら、これは「順次にアクセスするための一時的なオブジェクトなのですから、Iterator でラップする....というような思考プロセスを、実際私は取ってるわけですね。

考えてみれば、スナップショットで実装したとしても、これは後で「Aggregate を参照する」タイプの Iterator に変更する、という自由度も Iterator で返した場合にはあるわけです。普通の木構造の場合だと、幹要素への参照を葉要素が保持する必要があるので、ちょいと難しいかもしれませんが、「List でスナップショットして返す」という動作よりも、こっちの方が抽象的かつ柔軟なことには違いないでしょう。馴染みの Iterator でも意外な難しさを持っているものですよ!

半分妄想。「継続」をIterator 内部の参照として持つ Iterator って、「継続のある言語」だったら可能...ということになります。再帰の深いところから、継続返しで抜けてポイントしておき、next() のタイミングで、保持する「継続」をもう一度呼んで....とか、できちゃうかも。Ruby くらいでしかムリだろうね....

投稿者 : 杉浦 こずえ | 投稿日時 : 2008.04.23 22:52

facebookに投稿 このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク このエントリーをはてなブックマークに追加 この記事をクリップ! livedoorclip ユーザー数 BuzzurlにブックマークBuzzurlにブックマーク この記事をtweetする

わが槍の穂先を恐れる者は....

2008.04.21

Wer meinen Speeres Spitze furchtet, durchschreite das Feuer nie!
(わが槍の穂先を恐れる者は、この炎を決して越ゆるな!)

あ、これ呪文です。私がファイアーウォールを設定する時、コメントでいつもこれを入れます。元ネタ判る方いますかねえ....ヴァーグナーの「ヴァルキューレ」第3幕で、目覚めさせた人間の男に与える、という罰を自分を裏切った愛娘ブリュンヒルデに与える神ヴォータンが、せめてもの防壁としてその周りを炎の壁で守らせる...という場面で唱える呪文です(これが「ヴァルキューレ」の大詰めになります)。

まあ、洒落です。実際には「ジークフリート」の中で、「恐れを知らない」ジークフリートが、この炎の壁を越えてブリュンヒルデを手に入れます。たまにはこういう洒落を仕事にこっそり忍ばせてもいいでしょ!

元ネタ映像ブーレーズシェロー版、ヴォータン:ドナルド・マッキンタイア
 

 で....ですが、私は舞台一般に好きな方ですが、オペラって好きなんですね...最近 YouTube のオペラ関連動画投稿で楽しむ、というのを憶えちゃいました。やはりオペラの映像というと、日本で手に入るものはかなり限られています。ですから仕方のないことなんですけどもね。やはり海外のステージは凝った奇抜な演出がホントにすごいです。ちなみに上記引用映像は、1980年のバイロイト音楽祭で当時「クラシックの神」だったピエール・ブーレーズが指揮し、思い切った現代化を図った演出(シェロー)で上演した、史上最も画期的な上演の映像なんですけども、今は全曲版のDVDが現役でないようです(ワルキューレについてはあるみたいです)...やはりオペラだとマイナーなので商業的に引き合わないのでしょうか(今回シュミのネタでした)。

 

投稿者 : 杉浦 こずえ | 投稿日時 : 2008.04.21 09:48

facebookに投稿 このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク このエントリーをはてなブックマークに追加 この記事をクリップ! livedoorclip ユーザー数 BuzzurlにブックマークBuzzurlにブックマーク この記事をtweetする

使ってこその「新機能」

2008.04.19

ちょっと温故知新ネタです。Java が Java5 になって、もう結構時間がたちました。で...やはり私が仕事としているサーバサイドの開発でも、Java5 をデフォルト環境とする、というのはもはやアタリマエなのは、言うまでもありません(ま、開発ターゲットの問題で、互換性に神経質なためいまだに JDK 1.4 でやってる方はお気の毒ですけども、そろそろ減って来てはいるのでは?)。

というわけで、Java5 の経験も蓄積され、しかもイマドキ Java6 だってある(とはいえこっちのプロジェクトでの採用は私の仕事ではしてないです。ごめん)わけで、ちょっとまとめてみましょうか。要するに「Java5新機能を使ってる・使ってない星取表」です。私の場合、社内で「ちょっとややこしく例外的な機能」の担当になりがちなこともあって、「ビジネスなプログラマであっても、標準的な Java プログラマよりは少しいろいろな機能を試してみる機会も多いかな?」とも思うので、意味もあるんじゃないかと思いますよ!

1. Generics

まあ、これは一般的に使うでしょう。今までないのが不思議だったくらいですね。

2. Autoboxing/Autounboxing

Integer と int を同一視できる、って機能ですね。個人的には意外にちゃんと intValue() を呼んだり、new Integer(val) とかやって、あまり使わない傾向がありますね。私は気にしがちですが、気にせず使ってる人は多いです。

3. for 文の拡張

何やかんや言って、これは便利です。特に対象オブジェクトが配列でもコレクションでもループ記法が変わらない、というのが良いところですよね。甘口といえばそうですが、最近こればっかです。

4. static import

スタティックなメソッドとか変数を直接 import しちゃう機能です。これあまりメリットを感じないです...やはりその変数・メソッドが「どのクラスに所属しているか」を明示したほうが読みやすいでしょ(インスタンス変数について誤解がないのに、this.field とするようなものです)。多分今後も使わないんじゃないかな。

5. enum

仕事で使ってはいるけども、個人的にはそんなに大きなメリットを感じてない..というあたりでしょうか。「記号定数を抽象化する」というアイデアは良いと思いますが、enum が(往々にして内部)クラスを作るような実装である、というのが少し気になるあたりです....どうもそこらへんを美しくないと感じてるところがありますね。

6. 可変長引数

まあこれ、本当に甘口文法の部類で、単に可変長部分を配列で渡しても生成コード自体には違いがないです。そんなに使いませんが、そうは言ってもC出身ですから、String.format() は好きです(苦笑)。可読性、という点で使ったこともありますね。

7. 書式付き出力

要するに Stirng.format() です。C出身なので、ついつい使います。Cを知らない人は馴染みが薄いかなぁ...

8. 平行プログラミングライブラリ

java.util.concurrent.* です。特にコレの最上位に位置する Executor はワーカースレッドを簡単に作れちゃうものなので、重宝します。Webアプリでもリアルタイムに実行しない(副次的な)プロセスを作ることもありますから、これ「とっても便利!」と感じています。オススメです(けど少し勉強は必要ですよ!)。

9. アノテーション

個人的には、この機能にスゴク関心があります。自分で遊びのプログラムではよく使いますが、しかし仕事ではほとんど使ってないです(残念)。まあ、アノテーション対応のフレームワーク(Guiceどうだろ)を使った仕事ならば別でしょうが.....一度仕事でアノテーションプロセッサを作るとか、ガンガン使ってみたいものです。

10. M(X)Bean

この間仕事でも使いました。それは、Webアプリでリアルタイムにしない処理を担当するデーモン的プログラムで、それを「安全にシャットダウンするのにどうすれば?」という問題が出てしまって、「じゃあ MBean でシャットダウンする」という解決法にしました。「graceful なシャットダウン」ですね! そういう使い方がありますから、少し勉強してもいいかも。MBean は結構抽象的ですから「何をさせる」ときっちりターゲットを設定しないとイメージしにくいかな。

11. バイトコード操作

ライブラリとしては lava.lang.instrument パッケージ。クラスファイルに格納されるバイトコードを書き換えちゃう、という強烈な機能ですが、Java5 では「操作の窓口」を提供するだけで、「どう書き換えるか」の実装はサードパーティのものを使う必要があります。これは機能としては「今動いているプログラムのクラスの中身を書き換えちゃう!」という相当スゴイことが出来る機能なので、いわゆる「ホットスワップ」とかできるわけです....まあ、単に「クラスファイルを書き換える」というだけならば、Javassist でも使えば出来るので、instrument パッケージを使うのはちょっと特殊でしょうね。

....という感じでしょうか。それに比べると、JDK1.4 で追加されたパッケージ...の方がちょいと利用頻度が低すぎかもしれません。

1. Assertion

表明。これ、何であるか判らない、という人の方が多いのでは? いわゆる「契約による設計(プログラミング)」の考え方をチーム全体が理解しない限り、使い道はないでしょう。

2. New I/O

これも、何であるか?という感じのものでしたが、例の comet の話があって、ちょっとスポットが当たった感もあります。要するに select できるソケットを実装するのにこれを使い、他に代替策はありません。

3. Image I/O

javax.imageio パッケージ。これは画像を変換・加工するライブラリですが、あまり Webアプリで使うものではないですね....大昔 java.awt.ImageProducer とか使って画像加工した覚えがありますが、ホントは今はこれを使うもののようです。とはいえ、効率が悪くて どうも評判が悪い ようです....

4. Preference API

java.util.prefs パッケージ。これは設定データを統一的に取得するためのAPIなんだそうだ。Properties とか JNDI とかと似たことをする抽象レイヤーのようだ。どうもよく判らないです....(情報もホント少ないです)

5. Logging API

まあ、これ使ってるプロジェクトは珍しいでしょう。フツー Log4j だと思います。サードパーティでより強力な代替ライブラリがあり、標準API が忌避(deprecated!)された格好の例でしょう。

としてみると、必ずしも 「新機能」は使われるかどうか、判ったものじゃないわけですね。たとえそれが「標準!」と呼ばれていても....

投稿者 : 杉浦 こずえ | 投稿日時 : 2008.04.19 09:20

facebookに投稿 このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク このエントリーをはてなブックマークに追加 この記事をクリップ! livedoorclip ユーザー数 BuzzurlにブックマークBuzzurlにブックマーク この記事をtweetする

西暦2000年も他の年と大差があるわけじゃなし

2008.04.18

前回のエントリの続き...というわけではないですが、「数と意味」という哲学問題を考えてみると、一番参考になるかな、というのは私の愛読書なんですね。

それはまあ、ありふれてますが、グールドの「フルハウス-生命の全容」です。この本で有名なのは、「大リーグで4割バッターが絶滅したのは、能力の低下のためではなくて、全般的な能力の向上によるものである」という(若干客寄せ的な...そこらへん上手なエッセイです)小結論です。この話はTV番組とかでも紹介されてた覚えがありますから、知ってる方も多いかな?

しかし、実際にはこの本の大結論は、「時代とともに複雑な生物が登場してくるのは事実だが、進化というプロセスの中に、『より高度な生物を目指して』進化を方向づけるようなトレンドはない」ということです。それは、単に時代とともに多様性が豊かになったために、より高度な生活を営む生物が登場した、というだけのことであり、昔も今も、もっとも繁栄している生物はバクテリアであることに少しの変化もない、ということです。

要するに、人は「よく目立つ現象」を過大視します。4割バッターや、哺乳類から人類の登場といった、人間が「目を惹かれる」現象を以って、全体の作用の「代表」としがちです。しかし、それは頻繁に間違いなのです。4割バッターがこれほど特別に扱われるのは、単に人間が十進法で数を考えるから、「4割」を重大な壁だと考えているだけですし、あるいは西暦2000年を「ミレニアム」なんて呼んで特別視した...なんてのは、単に

人間が十進法に慣れているから

という理由であるに過ぎないわけです。そのような「心理学」の影響を抜きしてありのままを見る、というあたりに、グールドという人のクールなものの見方が感じられて、私は大好きなんですね。

で、この本ではグールド個人の病気の話から、一定の分布をもった値から、その「代表値」として適切なのは何か?という話をしています。よく人は「平均」を代表値として使いたがりますが、ほぼ同じ資格で使われる代表値としては、「モード」(最頻値=一番サンプル数が多い値)と「メジアン」(中央値=すべてのサンプルを順に並べた時に真ん中に来る値)があります。実際、分布が正規分布ならば、「平均=モード=メジアン」で3つの値は一致しますが、分布が偏っていたり歪んでいたりする場合(現実にはその方が普通です)には、具体的な分布の全容(フルハウス)に応じて、平均・モード・メジアンのうちで適切なものを選ぶべきなのです。実際、グールドは「自分のかかっている病気の診断から死亡までの日数のメジアンは8ヶ月だ」という医学書の記述から、その分布のかたちを推測し、一見「死亡宣告」みたいなその記述から希望を見つけ出す、という感動的な逸話を紹介しています....

というわけで、数を扱うプログラマ的に、グールドを「数とは?」という視点で読み直す、というのも面白いことだと思います。まだお読みでない方は5月の連休にでもどうでしょう?

 

投稿者 : 杉浦 こずえ | 投稿日時 : 2008.04.18 19:47

facebookに投稿 このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク このエントリーをはてなブックマークに追加 この記事をクリップ! livedoorclip ユーザー数 BuzzurlにブックマークBuzzurlにブックマーク この記事をtweetする

意味のある数、意味のない数

2008.04.16

先週の毎日新聞ですが、ニュース解説みたいなコラムで、

世論調査って当たってるの?

という「謎」について、答えてました。調査方法に工夫があって、年齢・性別などの偏りがないようなうまいサンプリングをしているのは言うまでもないことで、そのため1000人規模の調査の誤差は約3%となります。

更に、「新聞社によって内閣支持率が大きく違うのは?」という質問もありました。これは具体的な調査方法、「支持する」「支持しない」に加えて、「関心がない」を加えるかどうか、質問時に「強いていえば?」と聞き返すかどうか、といったあたりの影響があるので、これは「違うのが当たりまえ」というのが結論です。

まあ、「内閣支持率○%」とか数字が出てしまうと、何かそれが実体のある数値であるかのように思いがちです。しかし、その数値自体は言ってみれば「調査方法の関数」のわけです。ですから、同じ調査方法である同じ新聞社の中では、「支持率が上がった・下がった」と言うことに意味があるわけですが、それ自体の数値には大した意味はないわけです。

実際プログラマなんてしてますと、数にはいろいろあることに親しみます。「記号定数」というのは、「値が区別できる」だけの、意味のない数です。あるいは、順番を示すインデックスは、単に順序を示すだけで、その間に量的な関係はまったくありません。こういう順序数を拡張すると、たとえば「記号同士の結合力の強さ」に適当な値を与えてやって、結合力の大小関係によって処理を選択する....というような、インデックスではないにせよ、「順序の数」というのも使うことがあります。そして、いわゆる「量の数」もあるわけです。

ですから、「それ自体としては、とりたてて意味のない数」を使うことに関しては、プログラマは専門家かもしれませんね(苦笑)。とはいえ、そういう数でも「他と区別する、他と大小関係を比較する」というかたちで、非常に使い道が多いわけです。

そういう意味では、「ハッシュコード」という数は、そういう「意味のない数」の代表選手みたいなものでしょうか。あるオブジェクトを「とりあえずの大雑把な数」に変換して、それをそのオブジェクトの札として使う、という技法です。皆さんもよく使うハッシュテーブルは、「オブジェクトを一旦適当な変換関数で数に変えて、それをキーとして大雑把だけども効率のいい検索を行い、その後絞られた候補から一意に決まる厳密一致の検索をする」という手法です。Java とか使ってると、java.util.TreeMap を何となく「ハッシュテーブル」と呼びがちですが、TreeMap は一種の二進木で効率のいい検索をするアルゴリズムを使っているので、「ハッシュテーブル」と呼ぶのは不当だったりします....

逆に言うと、ハッシュテーブルでも、状況によっては「自前でハッシュコードを用意した方がいいケース」というのもあるわけです。複数のフィールドが一致するオブジェクトを検索する、というケースだと、

Map<MyObj,MyObj> map = new HashMap<MyObj,MyObj>();

  // 1,"test" の値を持つオブジェクトを検索したい
  MyObj search = new MyObj( 1, "test" );
  MyObj found = map.get( search ); 
  if( found == null ) {
    found = search;
    map.put( found, found );
  }
  ...... 

というような格好で使うことになって、ちょっと「サンプルによるクエリ」みたいな格好になります。この時、サンプルである search は、ハッシュコードを計算するためにオブジェクトとして作るようなものです.....ですから、MyObj では、ちゃんと equals(), hashCode() を上書きして、正しくハッシュで使えるようにセットアップしなければなりません。

たとえばこのハッシュは、

public int hashCode() {
    return no;
    // だろうが、
    // return (no * 12013 + (int)str.charAt(0) * 32157) % 32335; だろうが...
}

「同じ」と判定されるオブジェクトが、同じハッシュコードを返す、というだけの条件で、このハッシュコードを適当に決めてやればいいわけです。しかし、「違うオブジェクトが同じハッシュを返しても全然問題はない...」というのが、このハッシュコードの便利なところです。気楽に書けば大丈夫!!

というわけで、「意味のない数字」というのも、実は大変便利なものなのです。日常でもプログラマは「数字の魔術」に対する耐性が少しはある.....とイイですね!

投稿者 : 杉浦 こずえ | 投稿日時 : 2008.04.16 20:43

facebookに投稿 このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク このエントリーをはてなブックマークに追加 この記事をクリップ! livedoorclip ユーザー数 BuzzurlにブックマークBuzzurlにブックマーク この記事をtweetする

オブジェクトじゃないのに.....

2008.04.13

ちょっと遊びで書いているプログラムで、 Javaのリフレクションに関して

意外な書き方...

で?となったことがあります。それはですね、

java.lang.reflect.Method#getReturnType()

などの戻り値は、java.lang.Class なのですけども、このメソッドがたとえば、

public String getMessage() { ..... }

をリフレクトして java.lang.String を戻り値の Class として返すので、

if( String.class.equals(method.getReturnType()) {

と判定するのは勿論文句はありません。しかし、この method が、

public int getInt() { .... }
public double [] getDoubles() { ... }

をリフレクトするんだったら....となると、結構アレ?となるような書き方をしないといけません!

正解は、

if( int.class.equals(method.getReturnType()) ) {
} else if(double[].class.equals(method.getReturnType()) {

といった書き方です。ちょっと気味が悪いですね....

というのは、 Java の原理として、

int はオブジェクトではない!

という大原則があるのですが、このリフレクションの場合では、

オブジェクトだろうがプリミティブ型だろうが配列だろうが「統一的に」取り扱えなくてはならない!という事情で

int.class, double[].class

といったエキストラな書き方が導入された模様です。この書き方は、実は「classプロパティの参照」ではなくて、「int.class」という特別なリテラルだ、ということです....まあ、 Class#getMethod() するケースでの引数指定では、 java.lang.Integer で渡しちゃって全然問題がないので、これ気づきにくいんですよね....

まあ、Java というときちんとしているようで結構アバウトなところがありますから、こういうエキストラな書き方をこっそり導入したのかもしれませんね!(「リテラルだ!」と強弁するあたり、後付けっぽいですね、苦笑)

参考:JavaHouse「プリミティブ型の値をオブジェクトとして扱うには?」


投稿者 : 杉浦 こずえ | 投稿日時 : 2008.04.13 21:26

facebookに投稿 このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク このエントリーをはてなブックマークに追加 この記事をクリップ! livedoorclip ユーザー数 BuzzurlにブックマークBuzzurlにブックマーク この記事をtweetする

そんなん私の知ったことじゃないです....

2008.04.10

と、キレてるわけでは全然ないんですね。ちょっと今やっている仕事からのネタです。

集計表を作ることになりましたが、その元となるDBのテーブルの、どのフィールドを集計するのかが、どうもハッキリしません。また、今のバージョンではAフィールドを集計するかもしれないけども、次のバージョンでは「Aフィールド値-Bフィールド値」になるかもしれません...あるいは、別バージョンで「Cフィールド値+Dフィールド値」になるのかもしれません....

さて、困りましたね。その時、

じゃあ、どのフィールドをどう集計するか、は私の知ったことじゃないです。設定ファイルにどのフィールドをどう集計するかを記述できるようにして、その記述を元に集計できるようにしてやればいいんでしょ?

と「開き直る」のはどうでしょう?これ、デザパタでは Interpreter になりますね。

まあ、今ですと、特に Interpreter ってあまり頑張らなくてもできるライブラリとかあるわけです。世の中ススんでます。

  1. 1. Java 6 ならば(ま、Java5でも別立てで入れてもいいですが)、Rhino を使って JavaScript で記述する。
  2. 2. Janino みたいなミニJavaコンパイラライブラリを使って、Java で記述したコードを動的に実行する
  3. 3. ま、この程度だったら自前コードで解析する。+ - だけで括弧がない前提ならば、構文解析をほとんどサボれるし、単に左結合で順に評価していくだけで実行できてしまう。

それこそ、

report.collection.result=FieldA + FieldB

みたいなリソース記述(空白デリミタにすればかなり手が抜けます)で、集計指定ができる、というのは、

(どのフィールドを集計するかなんて)私の知ったことじゃないです。カスタマイズする人が決めてくださませ!

と開き直ったことによって、より柔軟で汎用的なプログラムが書けたことになるわけです。プログラマである私は、最後の最後までそのアプリの面倒を見きれるわけでも何でもないです。でしたら、「誰でも簡単にその動作を変更できるようにしておく」というのも、手の一つなんですよ!

 

投稿者 : 杉浦 こずえ | 投稿日時 : 2008.04.10 20:51

facebookに投稿 このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク このエントリーをはてなブックマークに追加 この記事をクリップ! livedoorclip ユーザー数 BuzzurlにブックマークBuzzurlにブックマーク この記事をtweetする

空にカードを放り投げて....

2008.04.09

空にカードを放り投げて、バラバラになった結果が、「ソートされているかどうか」をチェックする

というのは、一応ソートのアルゴリズムにならないわけではないです。つまり、すべての順列を作成し、最初から最後までソートされているものを選択する...というアルゴリズムだって、「ソートのアルゴリズム」として間違った解答を返すものではありません.....ただし、極めて邪悪なので Jargon File によると「ボゴソート」という名前がちゃんとついていたりします。

典型的な邪悪で恐るべきアルゴリズム(これと対照的に bubble sort は単に悪いアルゴリズムの総称)。

勿論私も「ボゴソートを使え!」などと邪悪なことを言うつもりはないのですが、前回の「パーミュテーション検定」の話とも関わって、考えることです。

勿論ボゴソートは無意味なアルゴリズムです。しかし、もし、あなたが1000台のコンピュータを同時に動かすことができ、あらかじめ長さ n のすべての順列のリストが用意されているのならば、その計算量は O(n) になってしまいます。これはクィックソートを使ったとしても、どうせ O(n log n) になってしまうのですから、実は「優れた」アルゴリズムだ、というサカサマの結論が得られる....というあたりに面白みを感じないでしょうか?

マルチスレッドモデルで、平行プログラミングを行う...というのは、まだまだそれほど一般的な話とまではいかないでしょう。どっちか言えば、平行性を持たせることで起きる障害を、なるべく少なくする、という「守り」の視点でマルチスレッドプログラミングを勉強することの方がずっと多いわけです。そうではなくて、

本質的に並行的なアルゴリズムを使い、そのメリットを充分に味わいたい!

というような「攻め」の視点が、ようやく現実的なリソースの問題として捉えても良いようになってきた.....のかもしれません。もし、1000台のコンピュータで同時に実行できるのならば、前回紹介したパーミュテーション検定はあらゆる検定に勝る検定力を持ち、かつそのどれよりも高速、ということになることだってあるわけです。私は「非決定性チューリング機械」という視点からの発想、というものを今後少し考慮に入れてもいいのかも....という感覚を持ち出したわけですね。

フツーのプログラムに使われるアルゴリズムは、決定的なアルゴリズムです。具体的なプログラムとして、

内部でちょっとしたシミュレーションみたいなことをして、比較したいくつかの結果のうち最高のものをとりあえず「解」として返す

というようなやり方をすることはほとんどありません(それでも TeX のレイアウトアルゴリズムでこういうのやってる...という話がありますが)。しかし、今後こういう手法も一般にアリ、になるのでは...などと空想するのはちょいと楽しいことです。

まあ、ここらへん、いわゆる「非決定性アルゴリズム」というタイプのアルゴリズムです。「必ず正解を返すアルゴリズムの計算量が多すぎるために、『必ず正解』ではないけども、間違う確率が極めて小さい高速なアルゴリズムを使う」というと、「素数判定アルゴリズム」はそういうタイプのもので、これは皆さんも日常的に使っている(RSA公開鍵暗号とかで活躍しますよね)わけです。ですから、必ずしも「わけのわからない神秘のアルゴリズム」というわけでもないでしょう。

そういう「非決定性」の入り口に、実は Erlang のプロセスモデルは役に立つ、のかも。「ボゴソートが邪悪でない世界」というのもあるのかもしれませんよ!

 

投稿者 : 杉浦 こずえ | 投稿日時 : 2008.04.09 20:20

facebookに投稿 このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク このエントリーをはてなブックマークに追加 この記事をクリップ! livedoorclip ユーザー数 BuzzurlにブックマークBuzzurlにブックマーク この記事をtweetする

Erlang: 組合せをやってみる

2008.04.05

さて、少しやる気も出てきたので、 Erlang の勉強編です。

私は大学生の頃、(実験系の)心理学科だった...というのもありまして、ゼミの内容はマジに「統計学」みたいなものでした。この中で取り上げられていたのが、「パーミュテーション検定(並べ替え検定)」という技法です。世の中にはいろいろな検定手法があるものですが、要するにこの「パーミュテーション検定」というのは、

帰無仮説=実験結果で実験群と対象群とでは、その平均値の違いは偶然の産物に過ぎない

というケースで、「ということは、実験群と対象群の差は見かけだけなんだから、その区別をなしにして、全部バラバラに組み合わせた時の平均値の違いを見る、と考えてみれば、その時に実験で得られた値を越えるような組み合わせは、いくらでもあるはず...」というかたちで考えるものです。だから、もしそれを超える平均値の出現頻度が5%以下ならば、「95%有意である」と結論できることになります。

実際、こういうケースでは普通、分散分析で分散の値を比較して検定するのですが、この「パーミュテーション検定」だと、分散分析の前提である「正規分布をしていること」を全然考慮しなくていいわけです。また、これだと、たとえば実験結果が「○×」でしか得られないような実験であっても、「○×の個数の偏り」が有意かどうかさえ、検定できてしまう(いわゆる「ノンパラメトリック検定」というものです)...というような、「検定」というものの根本から考えたような手法なのです。

ですから、これ「すべての組合せを求めて、それぞれに対して独立に判定を行い、その総計が全体に対して何%になるか」を計算する、という問題になるわけです....特に Erlang っぽいネタですよね!!!

でしかも、「組合せを生成する」というのはちょっと書いてみる価値のある、アルゴリズムの面倒なプログラムでもあります。実は Erlang の場合、順列を求めるだけなら、こんなコードで書けちゃいます。

perms([]) -> [[]];
perms(L) -> [[H{T] || H <- L, T <- perms(L -- [H])].

....とはいえ、これはちょい反則です。また、たとえばこの順列の結果から「前からk個取って、重複は無視/削除する」というアルゴリズムがないわけではないですが、順列の個数 >> 組合せの個数 なので、あまりに不効率です。まあ、ちょいとここから書いてみましょう。

-module(comb).
-export([comb/2,stripcomb/1]).
-import(lists,[map/2]).

%% 外部公開の呼び出しインターフェイス
%% comb( リスト, リストから「取る」数 )

comb([], _) -> [];
comb(_, 0) -> [];
comb(X,K) when is_integer(K), length(X) < K -> [];
%% 実際の計算がある部分は下請けに回す
comb(X,K) -> comb2( initcomb(X), K).

% 準備:[1,2,3] -> [{1,[2,3]},{2,[3]},{3,[]}] のようなタプルに変換する
initcomb([]) -> [];
initcomb( [H|L] ) -> [{[H],L} | initcomb(L)].

% タプル別に必要なだけ組合せを生成する
comb2([{H,I}|J],K) when length(H) == K ->  %% 長さが求めるだけになった
    stripcomb([{H,I}|J]);
comb2(X,K) -> comb2( nexts(X), K ).  %% まだまだ必要

%% タプル第1要素が求める長さになっているので、第1要素だけのリスト(結果)にする
stripcomb(L) -> lists:map(fun(X)->element(1,X) end, L).

%% タプル要素ごとのループを回す
nexts([]) -> [];
nexts([H|T]) -> nexts2(H) ++ nexts(T).

%% タプル第1要素について、1つ長くなった組合せを生成し、リストで返す
nexts2({_,[]}) -> [];
nexts2({A,[L|M]}) -> [{A++[L],M} | nexts2({A,M})].

少々ややこしいですが、こんなところかな。最初アルゴリズムを確認するために、Scheme で書いたのですが、これ Erlang の方がずっと見やすく、理解しやすいです。特に「タプル」があって、見た目上リストと区別できるので、「書きやすい」印象が強いです。これで、

comb:comb( [1,2,3,4,5,6], 3 ).

みたいな呼び出しで、 6C3 の組合せ([[1,2,3],[1,2,4],...[4,5,6]])が全部得られます。

では、今度は実際の検定です。測定自身は、使い捨て関数を書いて、引数で渡す、というかたちにしましょうか。

totalPerm:totalPerm(fun(X)->lists:sum(X)/length(X)>=25 end,[5,10,15,20,24,10,25,25,40,25],5).

これだと、実験群 5, 対象群 5 のデータに対して、実験群の平均値が 25 だったので、それを超える平均値の組合せの数と、その割合を返す、といった格好がいいでしょう。

{25,252,9.92063} %% {超える個数, 全体の数, パーセンテージ}

という感じですかね。では、実装は、

-module(totalPerm2).
-import(comb).
-export([totalPerm/3,calc/3]).

%% 外部公開。組合せを生成して下請けに回す
%% totalPerm( 判定述語, リスト, リストから「取る」数 )

totalPerm(Pred,L,K) -> X = comb:comb(L,K), tpLoop( Pred, X, length(X) ).

%% すべての組合せを試す
tpLoop(_, [], All) -> receiveResult(All,0,0); %% プロセス起動終りなので「受け取り」を起動
tpLoop(Pred, [H|T], All) -> invokeProcess(Pred,H), tpLoop(Pred, T, All ).

%% Erlang プロセスを起動する [自身のPid, 判定述語, 試す組合せ]
invokeProcess(Pred, L) -> spawn(totalPerm2,calc,[self(), Pred, L]).

%% Erlang プロセスで実行される関数。組合せを判定述語で評価し、
%% 結果を親に送り返す

calc(P, Pred, L) -> P ! evalList(Pred(L)).

%% 子プロセスで計算された結果を受け取る
receiveResult(All,All,Result) -> {Result, All, Result / All * 100}; %% 全部受け取った
receiveResult(All,Got,Result) -> receive Res -> receiveResult(All,Got + 1, Result + Res) end. %% まだあれば、更に受け取る

%% 述語での true, false を加算に変換
evalList(true) -> 1;
evalList(_) -> 0.

といったところでしょうか。意外にキレイに書けるものです....ちょっとびっくりするくらいです(まあ、他のマシンで依頼計算....まではしてないです。それでも大差はないですね)。このプログラムだと、Erlang のプロセスモデルを使い倒してます(それぞれの具体的な計算がプロセスで行われる)が、全然実行時にはストレスは感じません。もし、具体的な計算が重くなるようならば、分散させるのが良い手になってくるわけです....そういうあたりのバランス感覚が面白いことになるのでは?などとも感じました。

投稿者 : 杉浦 こずえ | 投稿日時 : 2008.04.05 19:35

facebookに投稿 このエントリーを含むはてなブックマーク このエントリーを含むはてなブックマーク このエントリーをはてなブックマークに追加 この記事をクリップ! livedoorclip ユーザー数 BuzzurlにブックマークBuzzurlにブックマーク この記事をtweetする

Java の static 変数は奥が深い....

2008.04.04

少し不調でエントリが久々になってしまいました。

で....マクロ話の続きです。

一応 Java では、条件コンパイル(みたいなもの)がないわけではないのです。これそんなに知られていないのには、理由がありますね。というは、

private static final boolean DEBUG = false;
..............
    if( DEBUG ) {
       log.debug( "診断メッセージ" );
    }

みたいなことをさせる...ということしか出来ないわけです。言い換えると、

  1. 1. static final で宣言された変数の値は、定数(みたいなもの)だ。
  2. 2. だから最適化の中で、常にfalseの条件で囲まれるなど、「非到達」と判定されたブロックは、コンパイル結果をあえてクラスファイルに含める必要はない!

という考え方のもとに、「条件コンパイル(みたいな)」結果が得られる、というものです。実際、出来たクラスファイルをテキストエディタで開いて、「診断メッセージ」があるかどうかを見てみれば確認できますよね。

....しかし、これ、いわゆる「条件コンパイル」とは大きく違います。たとえば、何か外部のライブラリが「あった」時に、そのライブラリを使う....というのを条件コンパイルでするケース(UNIX の C コードだと、./configure の結果からするこういうタイプの場合分けがメチャ多いですよね)では、Java では書きようがないです。それこそ設定ファイルベースでの Strategy を行う(それも共通化されたインターフェイスがあれば、の話)しかないです。

同じことをするライブラリなんだけども、いろいろな作者のものが勝手に乱立しインターフェイスに共通性がない....という場合だと、自分でそれらをラップして共通のインターフェイスに合わせるような Adaptor を作るしか手がないわけです。

要するに、

条件コンパイル機能 ⊃ Javaのstatic final 宣言による代用

なのは言うまでもないです.....ちょっと「条件コンパイル」と呼ぶにはためらわれるような実装です。で、この「代用」を行うこと自体が、実際には副作用を持ってしまった...という話があったりします。それは、

static final で宣言された変数を変更した時に、この変数を利用している他のクラスの明示的な再コンパイルが必要になる。

ということです。これは、static final 宣言=定数として、他のクラスをコンパイルする時にこれを値の参照ではなくて、その値をそのまま埋め込んでコンパイルしてしまい、なおかつこのクラスが「static final 宣言のあるクラスに依存する」という情報をクラスファイル中に保持しない...ということが原因になります。

ややこしいですね!これだったら無理してstatic final 変数を定数化しない方が良かったように思いますね....→参考

投稿者 : 杉浦 こずえ | 投稿日時 : 2008.04.04 17:31

カレンダー

<< 2008年04月 >>

    1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30      

最新のエントリー

最新のトラックバック

最新のコメント

Tag

バックナンバー