--/--/--

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

2008/04/26

至高のオブジェクト指向言語

オブジェクト指向についていくらか真剣に考えて、Javaこそがオブジェクト指向的には至高の美しさを持つ言語と考えるようになったので(実際そうだとは思いませんか?)、C++の参照やポインタの混在による汚さに惑わされているのだと思います。

参照が使えるときはいつでも参照を使いましょう - 神様なんて信じない僕らのために

オブジェクト指向言語という視点において、そりゃまぁ C++ よりは Java のほうが幾分かましなんだろうとは思う。俺は C++ を触った経験はほとんどないけど、話を聞く限りではオブジェクト指向がどうとかより、もっと他の部分に時間を食われ過ぎている印象を受ける。まぁそれは置いといて。

Java は Java で、"至高の" オブジェクト指向言語とは思えないなぁ。手続き型プログラミングな書き方を大いに許しているし。もちろんこれが1つの要因となって、現代の Java 人気があるんだろうけど。オブジェクト指向言語としてはどうなのかと。

もうちょっと挙げてみると、総称が腐っているところ、機能追加によって言語仕様が破綻しかけているところ、equals がオーバーライド可能であること、package は階層的構造を持つのにその辺を用いたスコープ指定がない、などなど。

equals について補足。Java ではインスタンスの比較に == か equals を用いるわけだけど、どちらもデフォルトでは参照の同値性、つまり同一性を比較する。すなわち、"a" == "a" は常に false を返す。また、new Object().equals(new Object()) も、常に false を返す。

とまぁここまではいいとして、これらには値としての同値性(以下同値性)を調べるという部分がすっぽりと抜けている。Java ではそういうことがしたいなら equals をオーバーライドせよということになっている。

いやいや待てと。equals ってのはそもそも同一性を調べるメソッドなのであって、同値性を調べるメソッドではないでしょう。メソッドの意味を大きく変更することになるオーバーライドはするべきではない。

"内部動作" を置き換えるオーバーライドは、本来の使われ方として正しいんだろう。繰り返しになるが、メソッドの "意味" を置き換えるオーバーライドは行うべきではない。それを奨励している Java は、オブジェクト指向言語としてはとても至高とは呼べない。

package について補足。Java のパッケージはファイルシステムべったりで、ディレクトリ階層がそのままパッケージ階層になる。別にそれはいいんだけど、そうやって階層構造を持たせることができるのなら、例えば「methodA を、この hoge.fuga パッケージ以下に位置する、hoge.fuga.piyo と hoge.fuga.pico パッケージにのみ公開」なんて指定できると思っちゃうのは必然ではないだろうか?

もちろん、今の Java ではこういうことはできない。package が階層構造である意味は、全くを持って意味が無い。ネームスペース以外の何者でもないくせに、ディレクトリ階層を要求する。

あと Object にメソッドが多いのも気になる。equals, hashCode, wait, notify, notifyAll, clone, toString 辺りが、なんで Object にあるのかと。同値性の比較が可能なインタフェース(Comparable)、条件変数として使用可能なインタフェース(Condition)、文字列かできるインタフェース、複製可能なインタフェース(Cloneable)、ってのをそれぞれ用意するべきだよ。いや、文字列化以外のは現にあるんだけど。Object にこれらのメソッドを持たせる利点が、どれほどあるのかと。

clone については、Cloneable インタフェースがありながらこの状態なわけで。そういう気持ち悪いところがたくさんある。

そんなわけで俺が思う至高のオブジェクト指向言語は Eiffel。オブジェクト指向入門で使われている、あの言語。実際に使ったことはないんだけど。本を読んだ限り、非常に言語仕様が洗練されている。総称もしっかりしているし、スコープ指定もしっかりしていてカプセル化も柔軟。オブジェクト指向言語としては、かなりの完成度かと。ただまぁ、API は少ないだろうから実用的では無いのかも。

2008/03/01

gmail の未読数を取得する

python の libgmail を使ったスクリプト。一定時間置きに走査する。未読数は $HOME/.gmail_stats に書き出される。

#!/usr/bin/env python

import sys
import time
import libgmail

output = '$HOME/.gmail_stats'
interval = 10 #ten seconds

username=''
password=''
try:
    username = sys.argv[1]
    password = sys.argv[2]
except IndexError:
    print '''
    Error: not enough arguments.

    Usage: python gmail_watcher.py username password
    '''
    quit()

account = libgmail.GmailAccount('%s@gmail.com'%(username), password)
account.login()

def output_msg_info(not_read_mails):
    port = file(output, 'w')
    try:
        #print out, not read mails count
        port.write('not read count:%d\n' % (len(not_read_mails)))
        port.flush()
    finally:
        port.close()

while 1:
    folder = account.getMessagesByFolder('inbox')
    not_read_mails = []
    for thread in folder:
        #info[1] == 1 -> not read
        if thread.info[1]:
            not_read_mails.append(thread)
    output_msg_info(not_read_mails)
    time.sleep(interval)

久々すぎて文字列の連結の仕方とか、関数の呼出しかたとか忘れてた。

ファイルに書き出すようにしたのは dzen に出力したかったため。直接文字列を渡すことが難しいので、ファイルに書き出すことでお茶を濁した。必要なければ標準出力に出すとか、その辺はいかようにもなる。

前は色々苦労したけど、結局実行直後にログイン→無限ループで走査が一番簡単。.xsession に、これを裏プロセスで実行させるように設定してる。

ちなみにファイルから未読数を取得するには、

sed -e 's/[^:]*://' $HOME/.gmail_stats

とかすりゃいい。てきとー。

2008/02/20

lambda

予定より全然進んでないけど、SICP をちびちび読み始めてようやく図形言語が終わりそうなところ。

図形の基本となるペインタオブジェクトが lambda で説明がつくということに感動を覚えた。lambda すごい。

さらに3章の最初のほうを先読みしてみると、lambda に状態変数が。

色々できるけど、lambda の仕様は1つで、値を受け取って、値を返すということだけ。これだけ仕様の小さな仕組みなのに、こんなに応用が効くことに美しさを感じる。

gentoo といい scheme といい、最近ミニマリズム脳になりつつあるな。

2008/02/16

ポインタ入門 for Java プログラマ

c のポインタを理解できない Java プログラマへ

俺もその中の一人だった。ポインタの仕組みは分かるんだけど、 c のポインタが理解できないというか。演算子がごちゃごちゃしてて分かりにくい。

そこで Java でポインタの真似をすることでこれを打開してみようと思いついた。 ポインタの仕組みさえ分かっておけば、基本的なところは簡単に真似できる。

ではまず、簡単な c のサンプルを見てみる。

 #include <stdio.h>

void proc(int a) {
    a += 10;
}

void procp(int *ap) {
    *ap += 10;
}

int main() {

    int a = 1;
    int *ap = &a;

    printf("a before : %d\n", a);
    proc(a);
    printf("a after  : %d\n", a);

    printf("-----------\n");

    printf("ap before: %d\n", *ap);
    procp(ap);
    printf("ap after : %d\n", *ap);
}

値渡しと参照渡しの違いを見せるサンプルである。ちなみにこのサンプルでは 引数をそのままいじっているが、これはよろしくない。真似するべからず。

では、Java でこれを真似してみる。

実は Java はプリミティブ型以外、全てポインタである。つまりぜーんぶ参照で、関数に受け渡したりなんなりするのも、プリミティブ型以外は全部参照渡しなのだ。これを利用すればいい。 もっとも、プリミティブ型のポインタだけを考慮するなら Integer とかのラッパークラスを 使えばいいが、もうちょっとユニバーサルな Pointer クラスを作る。

 public class Pointer<V> {

    public V value;

    public Pointer(V initial) {
        value = initial;
    }

}

指定された型の値を保持するだけのクラスである。

次に、C のサンプルを真似る Java のサンプルを作る。

 public class PointerSample {

    public static void main(String[] args) {
        int a = 1;
        Pointer<Integer> ap = new Pointer<Integer>(a);

        System.out.printf("a before : %d%n", a);
        proc(a);
        System.out.printf("a after  : %d%n", a);

        System.out.println("---------");

        System.out.printf("ap before: %d%n", ap.value);
        proc(ap);
        System.out.printf("ap after : %d%n", ap.value);
    }

    static void proc(int a) {
        a += 10;
    }

    static void proc(Pointer<Integer> ap) {
        ap.value += 10;
    }

}

結果は同じになる。a のほうは値が変わらず、ap のほうは値が変わる。 どちらの ap も内部の値が変わっているだけで、参照自体は変わって いないということに注意すれば、ポインタってなんなのさってことは分かるかと。

ただし次のようにはならないので、やはり厳密には真似できていない。

// さっきの c コードの続き
printf("a after  :%d\n", a); // 11 になる

これは ap が a のアドレスを直接指しているということを表す。 一方 Java の Pointer はそうではない。Pointer が new されてるから、そもそも元の a とは違うアドレスになってるということ。だから Java ではこの c コードのようなことにはならない。

真似しようとするなら、次のような感じ?当然実現できない。

int a = 1;
Pointer<Integer> ap = a.getAddress();

Pointer を a の参照として new Pointerするのではなく、a のアドレスを直接受け取る。

ポインタのポインタも c の構文のせいでややこしくなってるだけで、 Java で見れば一発で分かる。

Object o = new Object();
Pointer<Object> op = new Pointer<Object>(o);
Pointer<Pointer<Object>> opp = new Pointer<Pointer<Object>>(op);

以上の通り、ポインタってのは実は全然難しくない。難しいと感じるのは c の 構文のせいでしょう。要は参照なのだよ参照。

ちなみに、ここで作った Pointer クラスの似たようなものとして、java.lang.ref.Reference ってクラスが Java の標準APIにある。使ったことないけど。いらんメソッドがあっていやだったから、Pointer クラスを作ってみた。

2008/02/09

bash のヒアドキュメント

ちょっとはまった。上がダメで下が正しい。

function hoge () {
    cat << EOS
    foo
    bar
    EOS
}
function hoge () {
cat << EOS
    foo
    bar
EOS
}

原因は、終点文字列のインデントまで解釈しちゃうせい。だから以下でもいい。

function hoge () {
    cat << EOS
    foo
    bar
EOS
}

もちろん、foo bar のインデントも文字列として解釈され、表示に影響を与える。html の pre に近い。

教訓としては、ヒアドキュメント使うときはインデント使うなと。

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。