2008年12月20日 (土)

トップページにTooltip (jQueryを使ったオリジナル)

トップページにTooltip機能を付けた。Tooltipのスクリプトはいろいろと流通しているが、この数年の間に大きく様変わりしたJavascriptを久しぶりに使ってみようというわけで自分でTooltipを作成した。

[1] 2種類のTooltip、(1) フォーカスが当たったときだけ表示されフォーカスが外れたら消えるもの (2) 閉じるボタン付きで、フォーカスが当たったら表示されるが、消すのは閉じるボタンによるもの

[2] Tooltipに表示する内容は本体ページの中に記述できるようにする。しかもTooltipターゲットの近くに記述する。

[3] JavascriptのライブラリとしてjQuery1.2.6を使う。jQueryの日本語資料としてこちらを参照した。

[4] jQueryによるTooltip Coda Bubble のソースを参考も参考にした。

jsソースcssファイルはこちら。

Hytip_sample_1_2Hytip_sample_2 

[注]

(1) プログラミングインターフェースはprototypeによるTooltip protip2 に似ています。

(2) 動作確認は今のところIE7だけ

[機能拡張案]

(1) マウスカーソルがターゲットを横切っただけでTooltipが表示されてしまうのはうっとうしいので、一定時間ターゲット内にマウスカーソルが留まったときに表示する。

(2) IMGタグと組み合わせて画像の拡大表示。

2008年12月15日 (月)

JAVAのハッシュテーブル HashMap

stdext::hash_map [VC++2003]
System.Collections.Generic.Dictionary(K,V) [C#2008 Express Edition]
java.util.HashMap<K,V> [J2SE 1.6.0_03]

以上のコレクション(コンテナ)の使い方比較。(自分の知識を整理するためのメモです。)

ここではJAVAのハッシュテーブル HashMapの使い方をかんたんにまとめた。


キー・値ともにオブジェクト型の場合
キーとなるオブジェクトのクラスをKで表記
値となるオブジェクトのクラスをVで表記

[importパッケージ]
import java.util.HashMap;

[キーオブジェクトのクラスの定義]
2つのメソッドequalsとhashCodeを実装する。
実装しなくてもコンパイルは通るがHashMapは動作しない。

class K {
  ...........
 
  public boolean equals(Object obj){
   K other = (K)obj;
   return ....;
  }
 
  public int hashCode(){
   return ....;
  }
}

[HashMapオブジェクトの宣言]
HashMap<K, V>  kvh = new HashMap<K, V>();

[キー対値の登録]
kvh.put(new K(....), new V(....));

[検索]
K k = new K(....); // 検索キー
V v = kvh.get(k);
if (v != null){  // 見つかった
  v.Vのメンバー
} else { // 見つからない


キーが値型(int)、値がオブジェクト型の場合

キーとして java.lang.Integerクラスを使うか、独自にintをラップするクラス
を定義する。前者の場合、equals()とhashCode()メソッドは定義済み。

C#のハッシュテーブル Dictionary

stdext::hash_map [VC++2003]
System.Collections.Generic.Dictionary(K,V) [C#2008 Express Edition]
java.util.HashMap<K,V> [J2SE 1.6.0_03]

以上のコレクション(コンテナ)の使い方比較。(自分の知識を整理するためのメモです。)

ここではC#のハッシュテーブル Dictionay(K, V)の使い方を具体的にまとめた。


キー・値ともにオブジェクト型の場合
キーとなるオブジェクトのクラスをKで表記
値となるオブジェクトのクラスをVで表記

[using namespace]
using System.Collections.Generic;

[IEqualityComparerインターフェースの実装]
2つのメンバー関数を実装する。

    class KEqualityComparer : IEqualityComparer<K> {
        public bool Equals(K k1, K k2) {
            return ....;
        }
        public int GetHashCode(K k) {
            return ....;
        }
    }

[Dictionaryオブジェクトの宣言]
    Dictionary<K, V> kvd = new Dictionary<K, V>(new KEqualityComparer());

[キー対値の登録]
    kvd.Add(new K(....), new V(....));

[検索]
K k = new K(....); // 検索キー
    if (kvd.ContainsKey(k) == true) { // 見つかった
        // kvd[k] で値オブジェクトが参照できる
        kvd[k].Vのメンバー
    } else { // 見つからない

キーが存在しないとき kvd[k]はKeyNotFoundException例外となる。


キーが値型(int)、値がオブジェクト型の場合

[IEqualityComparerインターフェースの実装]
同様に定義する。

    class IntEqualityComparer : IEqualityComparer<int> {
        public bool Equals(int k1, int k2) {
            return (k1 == k2);
        }
        public int GetHashCode(int k) {
            return k;
        }
    }

[Dictionaryオブジェクトの宣言]
    Dictionary<int, V> ivd = new Dictionary<int, V>(new IntEqualityComparer());

[キー対値の登録]
    ivd.Add(int値, new V(....));

[検索]
int k = ....; // 検索キー
以下同様。

2008年12月12日 (金)

C++/C#/JAVA コレクション比較 【ハッシュテーブルの比較概要】

C++/C#/JAVAのハッシュテーブル
stdext::hash_map [VC++2003]
System.Collections.Generic.Dictionary(K,V) [C#2008 Express Edition]
java.util.HashMap<K,V> [J2SE 1.6.0_03]
の比較。(自分の知識を整理するためのメモです。)

各ハッシュテーブルの具体的使い方はそれぞれまとめるとして、ここでは大雑把な比較をまとめてみた。


[キー値の比較・キー値のハッシュ値の取得]
   ハッシュテーブルオブジェクトを構築する前に、キー値の同値性チェックまたは大小チェック、およびキー値のハッシュ値を取得する手段を構築しなければならない。
   
   C++のhash_mapの場合。
   stdext::hash_compareクラスを拡張(そのためにstd::lessクラスも拡張)することによる。
      2種類のoperator()をオーバーロードする
   
   C#のDictionaryの場合。
   IEqualityComparerインターフェースを実装することによる。
      Equals()とGetHashCode()の2つのメソッドを実装する。
   
   JAVAのHashMapの場合。
   キー値のクラスにメソッドを追加することによる。
      equals()とhashCode()


[ハッシュテーブルオブジェクトの構築]
   何れの場合もキーと値の型をパラメータとしてハッシュテーブルオブジェクトを構築するが、それに加えて上記のキー値比較・キー値のハッシュ値取得方法も指定する必要がある。
   
   C++のhash_mapの場合。
   stdext::hash_compareクラスの拡張クラスをハッシュテーブルのテンプレートパラメータとして指定する。
   
   C#のDictionaryの場合。
   IEqualityComparerインターフェースの実装クラスオブジェクトをハッシュテーブルのコンストラクタ引数として指定する。
   
   JAVAのHashMapの場合。
   キー値比較・キー値のハッシュ値取得方法はキー値の型定義に含まれているので、ハッシュテーブルの構築時に指定する必要はない。
   


[キー対値の登録]
   C++のhash_mapの場合。
   operator[]による
   
   C#のDictionaryの場合。
   Add()メソッドによる
   
   JAVAのHashMapの場合。
   put()メソッドによる


[検索と値の取得]
   C++のhash_mapの場合。
   find()メンバー関数でイテレータを取得し、イテレータで値にアクセス
   
   C#のDictionaryの場合。
   ContainsKey()メソッドで検索し、[]インデクサで値にアクセス
   
   JAVAのHashMapの場合。
   get()メソッドで値取得


[キー値としてintのような値型を使う]

   C++のhash_mapの場合。
   可能
   
   C#のDictionaryの場合。
   可能
   
   JAVAのHashMapの場合。
   java.lang.Integerクラスなどでキーをラッピングする。


[類似データ構造]

   C++のhash_mapの場合。
      std::map 木構造
      std::multimap キー重複可
   
   C#のDictionaryの場合。
      SortedDictionary(K,V) 木構造で要素を保持
      SynchronizedKeyedCollection(K,V) スレッドセーフ
   
   JAVAのHashMapの場合。
      LinkedHashMap<K,V> 要素の順序が挿入順序
      IdentifyHashMap<K,V> キーの同値性(equals)ではなく同一性(==)に基づく
      WeakHashMap<K,V> キーが使われなくなると自動的に削除される
      TreeMap<K,V> 木構造で要素を保持
   

2008年12月10日 (水)

C++/C#/JAVA コレクション比較 【インデックスによる要素アクセス】

std::vector [VC++2003]
System.Collections.Generic.List(T) [C#2008 Express Edition]
java.util.ArrayList [J2SE 1.6.0_03]

以上のコレクション(コンテナ)の使い方比較。(自分の知識を整理するためのメモです。)

テーマ【配列のようにインデックスによる要素アクセス


コレクション(コンテナ)変数の宣言
C++の場合
  std::vector<A> al;

C#の場合
        List<A> al = new List<A>();

JAVAの場合
  ArrayList<A> al = new ArrayList<A>();

以下の例でコレクション(コンテナ)は上記の様に宣言されているとする。
また、C++/C#/JAVAどの言語においてもインデックスの範囲は 0 ~ (要素数-1) である。


インデックスによる要素アクセス法
C++の場合
  al[インデックス].Aのメンバー
 
C#の場合
  al[インデックス].Aのメンバー
 
JAVAの場合
  al.get(インデックス).Aのメンバー


範囲外アクセス
C++の場合
  未定義(「例外」ではない。異常値を返したりプログラムが異常停止)

C#の場合
  System.ArgumentOutOfRangeException例外

JAVAの場合
  java.lang.IndexOutOfBoundsException例外


インデックスで指し示した要素の置き換え
C++の場合
  al[インデックス] = A(....);

C#の場合
  al[インデックス] = new A(....);

JAVAの場合
  al.set(インデックス, new A(....));
  (注:戻り値は指定された位置に以前あった要素)


インデックスの位置に要素をコレクション追加
元々インデックスの位置にあった要素とそれ以降の要素を後ろにずらす。

C++の場合
  for (it=al.begin(), i=0; it != al.end(); it++, i++){
     if (i == インデックス){
        al.insert(it, A(....));
        break;
     }
  }

C#の場合
  al.Insert(インデックス, new A(....));
  (注:O(n)操作, n=Count)

JAVAの場合
  al.add(インデックス, new A(....));


インデックスで指し示した要素をコレクションから削除
C++の場合
  for (it=al.begin(), i=0; it != al.end(); it++, i++){
     if (i == インデックス){
       al.erase(it);
       break;
     }
  }

C#の場合
  al.RemoveAt(インデックス);
  (注:O(n)操作, n=Count-インデックス)

JAVAの場合
  al.remove(インデックス);

C++/C#/JAVA コレクション比較 【列挙しながら要素削除】

std::vector [VC++2003]
System.Collections.Generic.List(T) [C#2008 Express Edition]
java.util.ArrayList [J2SE 1.6.0_03]

以上のコレクション(コンテナ)の使い方比較

テーマ【列挙子(イテレータ)で全要素を列挙しながら条件に該当する要素をすべてコレクションから削除する


C++の場合

std::vector<A> al;
std::vector<A>::iterator it;

// 要素を列挙しながら削除
it = al.begin();
while (it != al.end()){
    A& a = *it;
    if (aが削除条件を満たしているとき){
        it = al.erase(it);
    } else {
        it++;
    }
}


C#の場合

List(T).Enumerator構造体には削除のためのメソッドは無いし、List(T)にもList(T).Enumeratorと関連した削除メソッドは無い。
したがって削除しない要素を別のListに拾い出していくような方法しかない?。

List<A> al = new List<A>();
List<A>.Enumerator it;

List<A> al2 = new List<A>();
it = al.GetEnumerator();
while (it.MoveNext()) {
   A a = it.Current;
   if (aが削除条件を満たしているとき) {
   } else {
       al2.Add(it.Current);
   }
}
al = al2;


JAVAの場合

ArrayList<A> al = new ArrayList<A>();
ListIterator<A> it;

it = al.listIterator();
while (it.hasNext()){
    A a = it.next();
    if (aが削除条件を満たしているとき){
        it.remove(); // 最後のnext()で返された要素を削除する
    }
}

2008年11月28日 (金)

VC++2008のキャスト演算子(その1)

C++で分かりにくいのがキャスト演算子(static_cast/dynamic_cast/reinterpret_cast/const_cast)の使い分けである。
手元にあるC++の教科書「C++ 実践プログラミング」(Steve Oualline著 オライリー)は600ページぐらいあって単なる入門書ではないが、キャスト演算子の違いについて詳しく書いていない。

以下は復習を兼ねてのメモ書きです(その1)ではstatic_castとdynamic_castとの区別

「C++ 実践プログラミング」を資料1、「Boost  C++をチューンアップする最先端ライブラリ」(ビョルン・カールソン(著) 村上雅章(訳) ピアソン・エデュケーション)を資料2、として参照している。

使用したVC++は「Visual C++ 2008 Express Edition SP1」


[1] テストプログラム

class A {
public:
  virtual void a(){cout<<"A\n";}
};

class AA : public A {
public:
  void aa(){cout<<"AA\n";}
};

class AB : public A {
public:
  void ab(){cout<<"AB\n";}
};

A* a = new A();
A* aa = new AA();
A* ab = new AB();
static_cast<AA*>(aa)->aa(); // (1)
static_cast<AA*>(ab)->aa(); // (2)
dynamic_cast<AA*>(aa)->aa(); // (3)
dynamic_cast<AA*>(ab)->aa(); // (4)

[1-1] プロジェクトのプロパティ「ランタイム型情報を有効にする」を「いいえ(/GR-)」に設定したとき、(3)(4)でコンパイル時警告(warning C4541)は出るが、とりあえずコンパイルは正常終了する。
実行してみると、

・ (2)が実行できる。クラスAAのメンバー関数aa()が実行される。(これは結構すごい!)
・ (3)がランタイムエラーとなる。(プログラムを作った本人は、変数aaがAAのオブジェクトを保持していることに自信?があるのですけど)

[1-2] プロジェクトのプロパティ「ランタイム型情報を有効にする」を「はい(/GR)」に設定したとき、コンパイル時警告は無い。
実行してみると、

・ (1)(2)(3)(4)すべて実行できる。しかも、クラスAAのメンバー関数aa()が実行される。
・ (2)はともかく(4)が実行できてしまうのは何のためのランタイム型情報でdynamic_castなのだろうか。


[2] クラスAAだけ定義を変えた。

class AA : public A {
  int  aai;
public:
  void aa(){ cout<<"AA"<<aai<<"\n"; }
  AA() { aai = 1; }
};

[2-1] ランタイム型情報を有効にして実行してみると、

・ (1)(3)は期待通りの動きをする。
・ (2)はやはり実行できるが、aaiの値としてメチャクチャな値を使っている。static_castが危険であるといわれるのはこのあたりにあるのだろう。
(4)はランタイムエラーとなる。これが(自分の理解では)本来のdynamic_castの動きである
(2)と(4)の動きの違いがstatic_castとdynamic_castとの違いなのだろう。

[2-2] (4)のランタイムエラーについて。文献1は「不正な変換を試みると、例外がスローされ」(P434)としているが、文献2は「ポインタ型に対してdynamic_castが用いられ、変換に失敗した場合にはnullポインタが返される」(P52)としています。
C++の規格としてはどちらが正しいかは分かりませんが、(4)に関して言うと文献2の動きをしています。dynamic_cast自体は例外をスローせず、nullポインタを返しています。

2008年11月26日 (水)

VC++2008のRegex

VC++2008では「標準 C++ ライブラリ TR(technical report)1 拡張」において、Boostから取り入れられた正規表現ライブラリRegexが使える。以下は、これを使ってみて気がついた点のメモ書きです。

VC++2008は「Visual C++ 2008 Express Edition SP1」。 Boostは1.33.1で、VC++2003と組み合わせた。


[1] Boost版とTR版ではヘッダファイルと名前空間が異なるので、コンパイラのバージョンによってVC++2008以降はTR版を、それ以外はBoost版を使うようにした。

#if defined(_MSC_VER) && (_MSC_VER >= 1500)   // VC++2008以降
#include <regex>
#else
#include "boost/regex.hpp"
#endif

#if defined(_MSC_VER) && (_MSC_VER >= 1500)   // VC++2008以降
using namespace std::tr1;
#else
using namespace boost;
#endif

Boost版ではRegex用ライブラリファイルをリンクするようにプロジェクトのプロパティを設定する必要があるが、TR版ではその必要がない。


[2] 次のコード

regex reg1("(Colo)(u)(r)", regex::icase);
std::string s1 = "Colour, colours, color, colourize, COLOUR";
std::string s2 = regex_replace(s1, reg1, "$1$3");
std::cout << s2 << std::endl;

は、「Boost  C++をチューンアップする最先端ライブラリ」(ビョルン・カールソン(著) 村上雅章(訳) ピアソン・エデュケーション)のP137に載っている例で、イギリスの綴りcolourをアメリカの綴りcolorに変換するものだが、TR版では3行目でコンパイルエラーになる。

3行目を次のようにすると

std::string s2 = regex_replace(s1, reg1, std::string("$1$3"));

Boost版、TR版ともにコンパイルでき、実行結果は

Color, colors, color, colorize, COLOR

となる。

TR版ではフォーマット文字列として普通のC言語の文字列を受け付けるregex_replace関数が定義されていないようだ。


[3] regexのコンストラクタのflag_type引数に取り得る値はBoost版とTR版とで完全に一致していない。

たとえば、icaseは両方にあるが、perlはBoost版にあってTR版にない。

VC++2008のスマートポインタ

Visual C++の最近のバージョンでは「標準 C++ ライブラリ TR1 拡張」としてshared_ptrなどのスマートポインタが使える。

このスマートポインタはBoostから取り入れられたものらしいので、「Boost  C++をチューンアップする最先端ライブラリ」(ビョルン・カールソン(著) 村上雅章(訳) ピアソン・エデュケーション)のサンプルプログラムのいくつかをVC++付属のライブラリを使ってコンパイル&実行してみた。以下は気がついた点のメモ。

使用したVC++は「Visual C++ 2008 Express Edition SP1」


[1] Boostを使うソースとVC++付属のライブラリを使うソースの互換性を維持するため、ヘッダファイルのインクルード部分と、使用する名前空間の設定部分をコンパイラのバージョンによって切り替えるようにした。

#if defined(_MSC_VER) && (_MSC_VER >= 1500)   // VC++2008以降
#include <memory>
#else
#include "boost/shared_ptr.hpp"
#endif

#if defined(_MSC_VER) && (_MSC_VER >= 1500)   // VC++2008以降
using namespace std::tr1;
#else
using namespace boost;
#endif

実際のスマートポインタの使用法は同じになる。

shared_ptr<A> p(new A());


[2] boost::scoped_ptrは取り込まれていないが、なぜだろうか。shared_ptrがあれば十分と考えられたのだろうか。


[3] 上の文献のP20,P22,P25,P42にあるサンプルプログラムを実行してBoostによる場合と同じ動作をすることを確認した。(互換性テストにはならないでしょうが...)


[4] スマートポインタとは関係ないが、VC++2008ではassert.hを明示的にインクルードしないと、assert( )関数がコンパイルエラーになる。

2008年11月15日 (土)

文献「Boost C++をチューンアップする最先端ライブラリ」 【1】

「Boost C++をチューンアップする最先端ライブラリ」 (ビョルン・カールソン(著) 村上雅章(訳) ピアソン・エデュケーション)

 テスト環境:WindowsXP(SP2), VisualC++2003, Boost 1.33.1

サンプルプログラムをいくつかテストしてみて気がついた点を書いてみた。


[1] polymorphic_castのサンプル(53~54ページ)について、 VC++2003では「ランタイム型情報を有効にする(/GR)」を「はい」にしないと、コンパイルで「warning C4541... 」となる。


[2] lexical_castのサンプル(71~72ページ)について、

 (1)クラスlexical_castableのメンバ

virtual void read_(std::istream& i) const {
  i >> s_;
}

はコンパイルエラー C2679 となる。constを外した。

 (2)このままのソースだと実行時に数字を入力しても boost::bad_lexical_castがスローされてしまう。クラスlexical_castableにpublicメンバー

std::string operator()(){
  return s_;
}

を追加し、呼び出し側を

int i = boost::lexical_cast<int>(le());

とした。


[3] 第5章 Regexの130ページ。「Boostをビルドすれば、自動的にリンクが行われるようになるため(Windowsベースのコンパイラでも)、...」とあるが、VC++2003ではプロジェクトのプロパティで「追加のライブラリディレクトリ」に設定する必要があった。

自分の環境では「D:\Lib\boost_1_33_1\libs\regex\build\vc71」


[4] Anyのサンプル(152~154ページ)について、#include <string> を加えないとコンパイルエラー C2679 となる。

154~155ページのサンプルも同様。


[5] 第1章 Smart_ptrについて。shared_ptrなどに収容されたポインタを他のクラスにキャストしたいときどうすればよいのだろうか。 30ページにshared_ptrに対応するstatic_pointer_castの説明はあるが、サンプルプログラムが無いのは片手落ちの感。