Home日記コラム書評HintsLinks自己紹介
 

フィンローダのあっぱれご意見番 第141回「無整理法」

← 前のをみる | 「フィンローダのあっぱれご意見番」一覧 | 次のをみる →

ある書類を探すことになってしまった。 その書類は、半年以上前に見たような記憶がおぼろげにあるだけで、 どこに保存したとかいう情報は全く頭に残っていないのだ。 ただ、私の場合、あらゆる書類は引き出しに入れてる。 分類も何もしていない。 単に雑然とどんどん入れるのみだ。 大抵、ここに入れる書類は二度と見ることもない。 だから、現実的には、ゴミ箱に入れているようなものだが、 今回みたいなことが年に1度か2度あるから、 すぐにシュレッドしたりはしないのである。

それにしても、ターゲットの書類はたった2枚。 数千枚の書類の中からこの2枚を探すのである。 と聞くと、さぞ大変なことだろうと思うだろうか。 あるいは、 書類を分類してバインダーに閉じておけば簡単なのに、 とか思った人もいるかもしれない。 私も一瞬、ちゃんと分類しておくべきだったかな、 とか思ったものだが、 実は目的の書類は、ほんの1、2分で発見することができたのである。

なぜそんなに簡単に見つかったのか? この引き出しに、 使った書類を雑然と、 どんどん追加して入れていたのがポイントである。 その結果、 このスタックには、 時系列順にソートされた状態で書類が保存されていたのだ。 後は、その書類をいつ使ったのかという情報さえ思い出すことができれば、 かなり狭い範囲で存在する場所を絞り込むことができるわけである。 あるいは、前後の書類を見て、「この後のはずだ」とか「これより前だった」というようなことをキーにすることも可能だ。 これはバイナリサーチをしているようなので、 つまり、順に見ていく必要もないのである。 従って、結果的にはほんの1、2分で目的の書類をgetできたのだ。

  

超整理法というのをご存知だろうか。 野口悠紀雄氏が書いた本で有名になった方法である。 超整理法のポイントは、データを最終アクセス順に保存するということに尽きる。 UNIX 風にいえば、ls -lut でファイル一覧を見るような感じである。 要するに、私の机の引き出しは、それと似た状態になっていたわけだ。 厳密に言えば、 野口氏の超整理法は、 書類を封筒に入れて、時系列順に保存する、ということになっている。 それを考えると、 私のは、書類を封筒に入れなかっただけコストが削減できている所だけは、 優れているような気がする。 というか、 それって整理してないじゃん。 無整理法?

 

※ ls -lut は、最近アクセスしたものからファイル一覧を表示する。 ls -lt だと、アクセス時刻は考慮されないので、最近見たものかどうか分からない。

よく、机の上に書類を堆積させて地層状態にしている人がいるものだが、 あんな状態で書類が見つかるのだろうか。 意外と見つかるのである。 それは、机の上が超整理法的ワークスペースになっているからだ。

 

※ この場合は時系列だけでなく、 「右の奥にあったはず」のような位置も検索のための情報になる。

超整理法は、最後にアクセスしたら、それを最新の位置に保存することになっている。 最終アクセスタイムによるソートには、 ちょっと気になることもある。 例えば「1990年頃にあったなぁ」というようなデータは、1990年の頃のファイルから見つかる。これはいい。 ところが、それを一度見てしまったら、 そのデータ最新の位置に移動する。 例えば 2004 年のファイルにそれを移動したら、 次にそのファイルを探すときに「2004年に見たなぁ」ということが思い出せたら吉。 もし「1990年頃にあったなぁ」の方を思い出してしまったら、 そこには見つからない。2004年の位置に移動しているからだ。

  

もっとも、コンピュータ的には、 2004年に見たということと、1990年に作ったということは、別の情報として持つことができるから、あまりたいした問題にはならないのだが、 現実的に困るのは、全文検索とかした場合の後処理である。 例えば、grep などのコマンドで、 ディレクトリにあるファイルを機械的に全てスキャンしたら、 そこにある全ファイルを今アクセスしたことになる。 これは超整理法的にいえば、全ファイルを手前に引っ張り出してしまったことになる。 つまり、時系列がこの時点で破壊されてしまうのだ。 バックアップの時も同様の問題が発生しそうだが、 その場合には、 参照時刻を変更せずに処理するようなコマンドを使えば問題ない。

 

※ バックアップのために、そのファイルをアクセスしたら、 次にバックアップするときに「最近アクセスした」ことになってしまうのだ。

§

chat の最中に参加者に図や画像を見せたいことがある。 基本的に、 chat は短い文字列のコミュニケーションを想定しているので、 大きなサイズの画像を直接与えるような使い方ができない。 そこで、実際どういう感じで対応するかというと、 最近は chat に参加する人は自分のサイトを持っていたりするので、 どこかのWebページに画像を公開して、そのURLを紹介する、というような手法を使う。

ふと思いついた。 画像を指すURLは同じにしておいて、 その場の状況に応じて違う内容を表示させることは可能だろうか。 簡単である。 画像データをその都度上書きすればいい。 ただ、上書きしたら、古い情報は消滅する。 前の画像を見たいというときに、不便だということになる。 では、古い情報は古いものとして残し、 その都度違う情報を出すようなことは可能だろうか?

サーブレットを使って画像を送信する処理を書くことができる。 画像ファイルを返すようなサーブレットの処理の書き方は、 サーブレットの本を見れば大抵書いてあるはずだ。 例えば List 1 のような感じになる。 今回はJava。

---- List 1 getImages.java (画像イメージを返す servlet) ----

package myimage;

import java.io.*;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ImageBaseClass extends HttpServlet {

    public void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

        response.setContentType(getContentTypeString());
        outputImage(response.getOutputStream(), getImageFileName(request));
    }

    /**
     * request の内容に対応したイメージのファイル名を返す
     */
    private String getImageFileName(HttpServletRequest request) {
        return "dummy.jpg"; // 目的のイメージに対するファイル名
    }

    /**
     * このクラスが扱う画像タイプに対応する文字列を返す。
     */
    protected String getContentTypeString() {
        return "image/jpeg";
    }

    private void outputImage(OutputStream toClient, String filename)
        throws FileNotFoundException, IOException {
        InputStream in;

        in = new BufferedInputStream(new FileInputStream(filename));

        byte buf[] = new byte[1024];
        int size;
        while ((size = in.read(buf)) > 0) {
            toClient.write(buf, 0, size);
        }
        toClient.flush();
    }
}

List 1 の処理はサンプルとして書いたものであって、 実際に使うのであれば、もう少し手を加えなければならない所もある。 例えば、outputImage の処理は、例外をそのまま throws しているのだが、 Webで公開するのなら、例えばファイルが存在しない場合には、 何かそれなりのイメージを返すような処理を呼び出すとか、 その程度の工夫が必要になる。

サーブレットの実行時に呼ばれるdoGetの内容は、 ContentType を例えば「image/jpeg」のように指定する処理と、 あらかじめ指定しておいたファイルの中身をそのまま出力ストリームに書き出す処理だけである。 特に難しいことは何もしていない。 一つだけ凝ったことをしたのは、パッケージの構成である。 サーブレットは通常、「http://ほげほげ/servlet/」のような場所に置くことが多い。 多いといっても、servlet なんて文字列をここに使うのは、 セキュリティのことを考えると「私はサーブレットです」と宣言するようなことは避けた方がいいかもしれない。 とりあえず説明のためにそういうURLだと仮定しておくと、 普通にサーブレットを作ったら、 「http://ほげほげ/servlet/getImage」 のようなURLでアクセスすることになりそうだが、 これだといかにもサーブレットですと宣言しているようで、 いまいち面白くない。

そこで、例えば myimage というパッケージを作って、その下にサーブレットのクラスを入れておく(List 1 はそれを想定している)。 そのクラス名を「jpg」にすると、 URLは、 「http://ほげほげ/servlet/myimage.jpg」 となる。 何なら、servlet という名前も変更して、images とでもしておけば、 「http://ほげほげ/images/myimage.jpg」 でこのサーブレットをアクセスできる。 ここまで来たら、この画像をアクセスする人は、なかなかサーブレットであることに気付かない。 実際は、List 1 のようなクラスを getImages という名前にしておいて、 ここから派生させた jpg とか png というクラスを作り、 getContentTypeString() を override して、 jpg なら "images/jpeg"を、 png なら "images/png" を返すようにしている。

§

  

こうやってサーブレットを使って画像を送るようにしたら何かいいことがあるのか。 面倒なだけならこんな処理を書く意味はない。 例えば、サーブレット化することで、 リクエスト先の情報に応じて異なる画像を返すことが可能になる。 特定のURLからリンクされている時だけ画像を表示するとか、 認証されていれば高解像度の画像を、認証しなければ、モザイクのかかった画像【?】を返す、というような処理にすることができるのだ。 あるいは、日付情報を付加したリクエストに応じて、 その日の画像を返すというようなこともできそうだ。 まあその程度の話で、深い意味はないのだが。 面白い使い方があったら誰か教えてください。

 

※ 年齢が20歳以上でないと見せないような画像を作ることができるはず。 もちろん、アクセスする人の年齢が分かれば、だが。

(C MAGAZINE 2004年3月号掲載)
内容は雑誌に掲載されたものと異なることがあります。

修正情報:
2005-05-02 注釈追加版を公開。 2006-03-01 裏ページに転載。

(C) Phinloda 2004-2006, All rights reserved.