2006 年 7 月 31 日 21 時 50 分

MixiDiary+Iterator クラス


このアーカイブは同期化されません。 mixi の日記が更新されても、このアーカイブには反映されません。


今日は、list_diary.pl へのアクセス手段を提供する、
MixiDiary クラスを設計しよう。

MixiDiary は、各日記(項目)のコレクションの役割となる。
では、メソッドを列挙してみよう。

・コンストラクタ(session)
・iterator()

……以上。

list_diary.pl の現在の機能では、
全日記項目の数は取得できない。
そのため、getItemCount() は提供できないし、
getItem(index) のようなランダムアクセスも難しい。

となると、前から後ろに連続的にアクセスするしかないため、
その処理は列挙クラスに隠蔽することになる。
なので、メソッドは iterator() だけあれば事足りる。
getIterator() としたい所だが、Java 風にしておこう。

つまり、コレクションとしては最小限の実装となりそうだ。
Java 1.5 で言うところの Iterable インタフェース実装か。

========== MixiDiary.js ==========

// クラスのコンストラクタ
function MixiDiary(session) {
    this._session = session;
}

// 日記を列挙するイテレータを作成して返す
MixiDiary.prototype.iterator = function () {
    return new MixiDiaryIterator(this._session);
};

========== end of first half of MixiDiary.js ==========

ううむ。殆ど何もしてないクラスになったな。

次は列挙用のイテレータクラスだ。
実際に list_diary.pl にアクセスするのは、
このクラスの役割となりそうだ。

列挙クラスなので、メソッドは以下の通りとする。

・コンストラクタ(session)
・hasNext()
・next()

これも Java 風でいくことにしよう。
読み取り専用なので remove はないけどね。

列挙は、Java と C# で考え方が違う。

C# では、IEnumerator インタフェースで、
MoveNext() と、Current(読み取り専用プロパティ)であり、
続きがあるかどうか調査するメソッドがない。
また、Reset() という余計なメソッドがある。

逆に、Java は、現在のオブジェクトを取得するのと、
イテレータを先に進める処理が一体化してしまっている。
また、remove() という余計なメソッドがある。

個人的には、続きがあるかどうか調査、次へ進む、
現在のオブジェクトを取得の 3 セットがいいと思うのだが、
C# や Java の実装はそうはなっていない。
今回は、Java の方法を採用することにしよう。

昨日の列挙コードを埋め込んで実装すると以下のようになる。
MixiDiaryIterator は MixiDiary に密接に関係するので、
同じファイルの中に続けて入れることにする。

========== latter half of MixiDiary.js ==========

// MixiDiaryIterator クラスのコンストラクタ
function MixiDiaryIterator(session) {

    // MixiSession を記憶しておく
    this._session = session;

    // list_diary.pl を取得
    this._html = session.fetch("http://mixi.jp/list_diary.pl");

    // タイトル・ID・OwnerID を取り出す正規表現
    this._rex = new RegExp(
            "<td bgcolor=#F2DDB7>&nbsp;([\\w\\W]*?)</td></tr>"
            + "[\\w\\W]*?"
            + "<a href=\"view_diary.pl\\?id=(\\d+)&owner_id=(\\d+)\">続きはこちら</a>", "ig");
}

// 次の日記項目を返す
MixiDiaryIterator.prototype.next = function () {

    if (this._html == null) {
        // 本来は NoSuchElementException って感じ
        return null;
    }

    var match = this._rex.exec(this._html);
    if (match == null) {
        this._html = null;
        // 本来は NoSuchElementException って感じ
        return null;
    }

    // 仮実装。配列を返しておく。
    return [ match[1], match[2], match[3] ];

}

// 次の日記項目があるかどうか調べ、true/false を返す
MixiDiaryIterator.prototype.hasNext = function () {

    if (this._html == null) {
        return false;
    }

    var lastIndex = this._rex.lastIndex;
    if (!this._rex.test(this._html)) {
        this._html = null;
        return false;
    }
    this._rex.lastIndex = lastIndex;
   
    return true;
}

========== end of MixiDiary.js ==========

_html フィールドには HTML を入れておき、
g フラグを指定した正規表現で列挙する。

g(global) フラグを指定した場合、
exec メソッドで検査が成功すると、
正規表現の lastIndex プロパティが更新される。
そのため、昨日のような繰り返しの場合は、
意識せずに exec() が null を返すまで呼び出せば良い。

イテレータとして実装する場合、
チェックのための、hasNext メソッドが必要だ。
このメソッドは調査だけなので、状態を変えてはいけない。

正規表現の検査は、exec() の他に、test() がある。
test() は、正規表現に一致した場合に true を返すのだが、
単純に exec() を呼んで、戻り値が null かどうか
調べてるだけなので、lastIndex が更新されてしまう。

そのため、lastIndex プロパティの値を記憶しておき、
test 後に、元に戻す作業が必要となる。

また、exec() を呼び出して列挙が終了した際は、
無駄なマッチングを繰り返さないように、
_html フィールドをクリアしておき、
これが null であれば、false を返すようにしておこう。

ではテストコードを書く。

========== Main.js ==========

function main() {

    // ログイン
    var session = new MixiSession(
            "自分のメールアドレス", "パスワード");

    // 日記オブジェクトを作成
    var diary = new MixiDiary(session);

    // 日記列挙オブジェクトを作成
    var iterator = diary.iterator();

    // 項目を列挙
    while (iterator.hasNext()) {

        // 項目を取得(仮実装なので配列が返る)
        var item = iterator.next();

        // テスト出力
        WScript.Echo("================================");
        WScript.Echo("タイトル: " + item[0]);
        WScript.Echo("日記 ID: " + item[1]);
        WScript.Echo("所有者 ID: " + item[2]);
        WScript.Echo("================================");
    }

}

main();

========== end of Main.js ==========

うむ。シンプルになった。

では、実行してみよう。

================================
タイトル: 一覧表示と詳細表示
日記 ID: 187248888
所有者 ID: 2300658
================================
================================
タイトル: 正規表現
日記 ID: 186493095
所有者 ID: 2300658
================================

(以下省略)

よし、まずは問題なさそうだ。

list_diary.pl の 1 つのページから得られる情報は少ない。
明日以降は、何度かページにアクセスして、
日記を取得していく必要がある。

自動化プログラムとしての MixiSession の制約、
そしてエラー処理、ページャ処理など、
考えることは山ほどある。

少しずつ潰していくか。



Copyright (c) 1994-2007 Project Loafer. All rights reserved.