2011年11月16日水曜日

CentOSにNode.jsインストール時にエラー


CentOSにNode.jsインストール時にエラーが出たので忘備録として残します。

wget http://nodejs.org/dist/node-v0.6.1.tar.gz
tar zxvf node-v0.4.12.tar.gz
./configure
make    # ここでエラー発生


エラー内容
ImportError: No module named bz2:
 File "/usr/local/src/node-v0.6.1/deps/v8/SConstruct", line 37:
  import js2c, utils
 File "/usr/local/src/node-v0.6.1/deps/v8/tools/js2c.py", line 36:
  import bz2


別のマシンに入れたときはこのような問題は発生せず。
python絡みで問題が出ているようなので、そのあたりの違いを確認すると
問題が発生したマシンには、python2.7がソースからインストール済み。
問題が発生しなかったマシンには、CentOSのデフォルトであるpython2.4系がインストールされている。

とりあえず、pythonを更新して再度インストールを試みる。
同時に、import bz2で問題が発生しているということで、bzip2-develをサクっと更新。

yum install bzip2-devel
wget http://www.python.org/ftp/python/2.7.2/Python-2.7.2.tgz
tar xzvf Python-2.7.2.tgz 
cd Python-2.7.2
./configure 
make
make install

そして、再度Node.jsをインストールしなおすと、問題なく完了。
いままで幾つかの環境にNode.jsをインストールしていて、初めてのエラーでちょっと焦った。


(追記)
githubのREADMEにpythonのバージョン指定があったことに気がついた。
Unix/Macintosh (requires python >= 2.5.2):
python2.4系でも入ってたのはいつのバージョンだ?ともかく、READMEはちゃんと読もう(自戒)

2011年11月11日金曜日

JavaScriptでオープンクラス


最近もっぱら、JavaScriptの勉強を行なっています。
今日は、JavaScriptにもオープンクラスが存在したことに対する備忘録です。

Rubyistにとっては当たり前の機能として存在する「オープンクラス」

PHPerやJavaプログラマーには聞きなれない概念ですが、
ひとことで言うと、「既存のクラス定義を任意のタイミングで変更可能」な機能です。
今更確認することでもないのですが、Rubyの場合は単純に
以下のように既存クラスの定義を変更できるかとおもいます。

class Array

  def tail
    last(length-1)
  end

end

scalaのListに存在する[tail]メソッド(先頭以外の要素からなるListを返す)
をrubyの組み込みクラスである[Array]クラスに追加して見ました。

このような仕組みがJavaScriptでも実現できます。

var arr = [1, 2, 3];

// last()を実行(そんな関数は存在しない)
arr.last();  // TypeError: Object 1,2,3 has no method 'last'

// Arrayクラスに変更を加える
Array.prototype.last = function() { return this[this.length-1]; }

// メソッドが追加された
arr.last();  // 実行結果 [3]


JavaScriptはブラウザ間の挙動の差に泣かされ、いままで避けれるだけ避けてきましたが、
Node.jsやTituniumなどのブラウザの世界を超えてJavaScriptが活躍し始めた所で逃げるのをやめることにしました。
勉強をすすめるうちに、なかなかどうして楽しい言語だと気付かされます。
とりあえず、JavaScriptはprototypeをきちんと学ぶと、色々できることが最近分かって来ました。
ん~、かなり面白いです。


=================================

おまけとして、少し話題に出てきたScalaでオープンクラスを実現する方法

Scalaは静的型付け言語のため、オープンクラスというものは機能として存在しません。
しかし、暗黙的型変換を利用することによって、同等のことが実現可能です。

ScalaのListクラスの[head]メソッドと同等のメソッド[h]を作成してみます。

class List2[T](xs : List[T]){ 
  def h: T = xs.head
}

implicit def list2List2(xs: List[Any]) = new List2(xs)

val l = List(1, 2, 3, 4, 5)
l.h // res1: Any = 1

ListクラスがList2に暗黙的な型変換が行われ、メソッド[h]がエラーなく実行されているのが
わかるかと思います。

2011年11月10日木曜日

JavaScriptのthis参照について


JavaScriptに限らず、JavaやPHPなどにも出てくる[this参照]。
JavaScriptでも多言語と同じ感覚で使っていてまた問題も特になかったのですが、
きちんと勉強すると、関数内のthis参照は関数の呼び出し方で変化するということを知りました。

// Objectを定義
var obj = {
    x : 3,
    func : function() { console.log('x : ' + this.x); }
}

// obj.funcをトップレベルの変数にコピー
var f = obj.func;


// f(); を実行する
f();   // 実行結果 => x : undefined

// トップレベルに変数xを定義
var x = 5;

// 再度f();を実行
f();   // 実行結果 => x : 5


感覚的には、12行目の関数の結果にて、
[x : 3]と表示してもらいところですが、結果は[x : undefined] となってしまいました。

これは、関数の実行がトップレベルにて行われているため、
thisはトップレベルを表すことになってしまった為です。

そのため、15行目に変数xを定義してf();を実行すると、実行結果に影響が出るという結果に。

恐らく素直なコーディングを行なっていればこのような動きには気付かないかと思いますが、一つ勉強になりました。


2011年11月8日火曜日

AndroidのListViewにもUITableViewのsectionが欲しい


AndroidのListViewには、iPhoneのUITableViewのsectionのような概念がありません。
これをAndroidのListViewで実現するためのコンポーネントを簡単に作って見ました。

public class MultiSectionAdapter extends BaseAdapter {

    /** セルがセクションヘッダーであることを表す値 */
    public final static int TYPE_SECTION_HEADER = 0;

    /** 各セクションのValue部を保持するアダプターの集合 */
    public final Map sections = new LinkedHashMap();

    /** セクションヘッダーアダプター */
    public final ArrayAdapter headers;

    public final Context context;


    public MultiSectionAdapter(Context context) {
        super();

        this.context = context;
        headers = new ArrayAdapter(context, R.layout.list_section_header);
    }

    @Override
    public int getCount() {
        // ヘッダーを除いて、セルの数を返す
        int total = 0;
        for(Adapter adapter : this.sections.values()) {
            if (adapter == null) {
                continue;
            }
            total += adapter.getCount() + 1;
        }
        return total;
    }

    @Override
    public Object getItem(int position) {
        for(Object section : this.sections.keySet()) {
            Adapter adapter = sections.get(section);
            int size = adapter.getCount() + 1;

            if(position == 0) return section;
            if(position < size) return adapter.getItem(position - 1);

            position -= size;
        }
        return null;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public int getViewTypeCount() {
        int total = 1;
        for(Adapter adapter : this.sections.values()) {
            if (adapter == null) {
                continue;
            }

            total += adapter.getViewTypeCount();
        }
        return total;
    }

    @Override
    public int getItemViewType(int position) {
        int type = 1;
        for(Object section : this.sections.keySet()) {
            Adapter adapter = sections.get(section);
            int size = adapter.getCount() + 1;

            if(position == 0) return TYPE_SECTION_HEADER;
            if(position < size) return type + adapter.getItemViewType(position - 1);

            position -= size;
            type += adapter.getViewTypeCount();
        }
        return -1;
    }

    @Override
    public boolean isEnabled(int position) {
        return (getItemViewType(position) != TYPE_SECTION_HEADER);
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        int sectionNum = 0;
        for(Object section : this.sections.keySet()) {
            Adapter adapter = sections.get(section);
            int size = adapter.getCount() + 1;

            if(position == 0) return headers.getView(sectionNum, null, parent);
            if(position < size) return adapter.getView(position - 1, convertView, parent);

            position -= size;
            sectionNum++;
        }
        return null;
    }

    /**
     * セクションヘッダーと対となるアダプターをセットする
     *
     * @param section セクションヘッダー
     * @param adapter セクションアダプター
     */
    public void addSection(String section, Adapter adapter) {
        this.headers.add(section);
        this.sections.put(section, adapter);
    }
}


そして色々と端折ってますが、このAdapterは以下のように利用します。

public class MainActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        ListView list = (ListView) findViewById(R.id.listView);

        // MultiSectionAdapterに追加するセクション その1
        @SuppressWarnings("serial")
        List names1 = new ArrayList(){{add("相田"); add("石田"); add("上田");}};
        ArrayAdapter adapter1 = new ArrayAdapter(this, android.R.layout.simple_list_item_1, names1);

        // MultiSectionAdapterに追加するセクション その2
        @SuppressWarnings("serial")
        List names2 = new ArrayList(){{add("加藤"); add("木村"); add("久保");}};
        ArrayAdapter adapter2 = new ArrayAdapter(this, android.R.layout.simple_list_item_1, names2);

        // ヘッダー部のラベルと、対応するAdapterを渡す
        UserAdapter adapter = new UserAdapter(this);
        adapter.addSection("ア行", adapter1);
        adapter.addSection("カ行", adapter2);

        list.setAdapter(adapter);
    }
}

class UserAdapter extends MultiSectionAdapter {
    public UserAdapter(Context context) {
        super(context);
    }
}

  • ヘッダー部に任意のViewを指定できない
  • UITableViewと比較して、footer部がない

などなど、幾つかの不足・問題点はあるのですが、
現実の要件としてはこれで十分事足りるのではないかと思います。

2011年11月3日木曜日

初投稿

備忘録として、IT関連のことを書いていきます。