日曜日, 6月 23, 2013

Algorithm 問題を解く 6 - ハッシュ

ハッシュ

ハッシュはバイナリサーチよりも効率的に検索をしたい場合に必須になるアルゴリズムである.
手短にハッシュを説明すれば,

  1. 一程度のメモリ領域をハッシュバケットとして確保
  2. ハッシュコードを要素から算出し,そのハッシュコードを検索キーとしてハッシュバケット中の1スロットに挿入

となる.例えば以下のハッシュバケット領域があったとして
0x0000 .. NO DATA
0x0001 .. NO DATA
0x0002 .. NO DATA
あるデータ X を保存したいとする.Xのハッシュキーが2だったらば,上記のハッシュバケットは
0x0000 .. NO DATA
0x0001 .. NO DATA
0x0002 .. { X }
となる.
検索はハッシュキーを元にデータを検索し,計算量はO(1)であり,2分検索の平均O(logN)より効率的である.

ハッシュコリジョン

当然,別のデータが同じハッシュキーを持つことがある.そういった場合はそのハッシュキーのスロットは大抵は循環リストのデータ構造を持つか,または別の空きスロットを見つける(オープンアドレッシング)する.最悪の場合の計算量はO(n)となる.


問題

Given a dictionary of English words, return the set of all words grouped into subsets of words that are all anagrams of each other.

Algorithms for Interviews より引用.


解答

#include <iostream>
#include <map>
#include <list>
using namespace std;

list<string> dict;

void init_dict() {

 dict.push_back( string("dog") );
 dict.push_back( string("god") );
 dict.push_back( string("redrum") );
 dict.push_back( string("murder") );
 
}

void analyze( list<list<string>> ls ) {

 list<string> l;
 string s;
 map<int,list<string>> m;
 list<string>::iterator iter;

 for( iter = dict.begin();iter != dict.end();iter++ ) {

  int sum = 0;
  s = *iter;
  for( int i = 0;i < s.length();i++ ) {
   sum += s[i];
  }
  if( m.find(sum) == m.end() ) {
  
   l.clear();
   l.push_back(*iter);
   m[sum] = l;

  }
  else {

   l = m[sum];
   l.push_back(*iter);
   m[sum] = l;

  }

 }

 map<int,list<string>>::iterator mapIter;
 pair<int,list<string>> t;
 for( mapIter = m.begin();mapIter != m.end();mapIter++ ) {
  t = *mapIter;
  cout << t.first << ":{";
  list<string>::iterator li = t.second.begin();
  for(;li != t.second.end();li++)
   cout << li->data() << ",";
  cout << "}" << endl;

 }



}


int main() {

 init_dict();
 list<list<string>> ls;
 analyze(ls);
 list<string>::iterator iter;
 for( iter = dict.begin();iter != dict.end();iter++) {
  cout << (*iter).c_str() << endl;
 }

 return 0;
}


木曜日, 6月 20, 2013

C++ 標準関数 おさらい 1 .. strtol, strtod, strtof

充実のC++標準ライブラリ

C++標準ライブラリは大変優れています,というのはいうまでもない....勿論boostを使わないとできないことも多々あるが,それでもまずC由来のライブラリと合わせてこの標準ライブラリを使いこなせるようになれば大抵のことは出来るはず.

strtol, strtod, strtof - 文字列から数値型に変換

strtol, strtod, strtof -これらの関数はそれぞれconst char*をそれぞれlong型,double型,float型に変換する.strtolは指定した底から10進数に変換することもできる.似たような関数にatoi,atof,atolがある(atodはない,atofはdoubleを返す)が,N進数パース等といった機能も含めるとstrto系のほうがお勧めだ.ちなみにヘッダはcstdlid. long longに変換するstrtoll,long doubleへのstrtold, unsigned long longへのstrtoullもちゃんとある.

Javaでは

それぞれLong.parseLong(), Double.parseDouble(), Float.parseFloat().


サンプル

O'reillyのC++ Cookbookのサンプルコードをほぼ引用したうえで,自作のmyhex2int()で16進数->10進数変換するコードを足してみた.底変換のコードはもっときれいにかけるはずだがご愛嬌.

#include <iostream>
#include <string>
#include <cstdlib>
#include <cmath>
#include <map>

using namespace std;

static map<char,unsigned int> nummap;

long hex2int(const string& hexStr){

 char *offset;
 if(hexStr.length() > 2) {
  if(hexStr[0] == '0' && hexStr[1] == 'x')
   return strtol(hexStr.c_str(), &offset,0);

 }
 return strtol(hexStr.c_str(),&offset,16);
}

static void init_map() {

 for( char ch = 0x30;ch < 0x40;ch++ )
  nummap[ch] = ch-0x30;
 for( char ch = 0x61;ch < 0x67;ch++ )
  nummap[ch] = ch-0x31;

}

long myhex2int(const string& hexStr){

 double repeat = 0;
 int t = 0;

 init_map();
 int sum = 0;

 if(hexStr.length() > 2 && hexStr[0] == '0' && hexStr[1] == 'x') {
  for(int i = hexStr.length() - 1,repeat = 0;i > 1;i--,repeat++){
   sum += ( nummap[hexStr[i]] * pow(16.0,repeat) );
  }
 }
 else {
  for( int i = hexStr.length() - 1,repeat = 0;i >= 0;i--,repeat++){
   sum += ( nummap[hexStr[i]] * pow(16.0,repeat) );
  }
 }
 return sum;

}

int main() {

 string str1 = "0x1234";
 cout << hex2int(str1) << endl;
 string str2 = "1234";
 cout << hex2int(str2) << endl;
 string str3 = "QAFG";
 cout << hex2int(str3) << endl;
 cout << "--- my(hex) ---" << endl;
 cout << myhex2int(str1) << endl;
 cout << myhex2int(str2) << endl;
 
        return EXIT_SUCCESS;
}

土曜日, 6月 15, 2013

UNIXコマンド One liner 4

あるディレクトリ以下の,rootユーザ又はルートグループが所有しているファイル又はディレクトリを再帰的に探し表示
この結果はノイズを含む可能性がある.

find *|xargs ls -al|egrep root

月曜日, 6月 10, 2013

TopコマンドのRES,VIRT,SHR

Topコマンド

管理者のみならず開発者,そして私のようなアーキテクトにとっても良い友人のTopコマンド.その友人がもたらしてくれる情報の内,VIRT,RESそしてSHRの意味合いをもう一度おさらいしておく.

VIRT (Virtual Image )

VIRTはプロセスの
  • コード
  • データ
  • ライブラリ
  • スワップアウトしたページ
  • メモリマップドファイル
の内,まだ使用されていないものの合計であるわけで,全てがメモリを指しているわけではないので注意.C/C++コードでただnew或いはmallocしただけだと,確保した分がVIRTのサイズに上乗せされる.Windowsでいうところのコミットチャージか.

RES (Resident Memory)

RESは実際にプロセスに利用されているデータの総計を示している.Javaで大きなヒープを確保していたり,C/C++でたくさんnewあるいはmallocしているとRESとVIRTの差が大きくなる.Windowsでいうところのワーキングセットである.

SHR (Shared Memory)

ライブラリなど,他のアプリケーションからも利用される可能性のあるメモリの総計である.

蛇足 - PSで指定したプロセスの情報だけ見る

ps -pオプションで指定したプロセスの情報を見ることが出来る.

UNIXコマンド One liner 3

特定のプロセス(ここではbigというプログラム)のPIDとパスをreport.txtにダンプし表示

egrep -vでps -ef中のegrepのPIDを除外.


ps -ef|egrep ./big|egrep -v egrep|awk 'print{$2,$8}' > report.txt;cat report.txt

UNIXコマンド One liner 2

特定のワード(ここではGETをPOSTに)を置換して別名ファイルとして保存

rm -fr conv;mkdir conv;for f in `find * -prune -type f`;do sed -e 's/GET/POST' $f > conv/%$f.conv;done

火曜日, 6月 04, 2013

UNIXコマンド One liner 1

UNIXシェルは奥深い

UNIXシェルの奥の深さは底知れない.あなたがもし何かの処理の為のスモールプログラムを書くつもりならば,UNIXシェルだけで95%のことは出来る,ということをまず考えたほうが良い.何十行のプログラムやスクリプトでできることはシェルの一行でできるはずだ.

一行で色々やってみる

以下は一行で色々やってみたという参考例.実際に私の仕事のうち,データ解析,大量のファイルデータ処理等はR/SQL/スクリプトを書く以前に,大概ワンライナーシェルスクリプトの組み合わせである程度まで出来る.

特定のファイルのサイズ総計を表示

find *Neko*|xargs ls -al|gawk '{s+=$5} END {print s}'

全ての.txtファイルのサイズが大きい順トップ10のファイル名とサイズを表示

find **/*.txt|xargs ls -alSr|awk '{print $9,$5}'|tail -10

全ての.txtファイルのサイズが小さい順トップ10のファイル名とサイズを表示

find **/*.txt|xargs ls -alSr|awk '{print $9,$5}'|head -10

又は

find **/*.txt|xargs ls -alS|awk '{print $9,$5}'|tail -10

あるgzipされたファイルの,パイプで区切られたデータの4行目の内容を使ってソート,多かったデータの上位5件を表示

zcat data.txt.tar.gz|cut -f4 -d"|"|uniq -c|sort -nr|head -5


月曜日, 6月 03, 2013

sudo設定1 .. 権限の委譲とパスワードプロンプトの抑制

権限移譲は慎重に

sudoを効果的に使えることは,ある程度以上の規模のサーバ管理を複数人で行うとすれば必要になってくるのだろう.今回は権限の委譲とパスワードプロンプト抑制というテーマである.
ターゲットは極私的な,しかも完全に安全なところに配置されているラボラトリサーバである.会社や大学の一般のサーバ上では権限の委譲はある程度良いにしてもパスワードプロンプト抑制はおこなわないように.

グループ作成

ここではADM_LIMITEDというグループを作成し,特定のパワーユーザをこのグループに属させ,そのグループを経由してroot権限を委譲する.

まずは以下のコマンドでグループ ADM_LIMITEDを作成,そしてユーザをそのグループに属させる.

groupadd ADM_LIMITED
usermod -G ADM_LIMITED tanuchan


新規ユーザをそのままグループに属させたいならばuseraddの-gオプションを使う.
とりあえずidコマンドでユーザのグループ情報を確認しておこう.グループは基本は追加すること.リプレースは危険だ.

sudoviで編集

sudoの設定ファイルは/etc/sudoersであるが,直接ファイルを編集すべきでない.代わりにvisudoコマンドを使うべきである.visudoコマンドは編集後自動的にバリデーションを行ってくれて,バリデーションに失敗した時は what now? と編集に戻るかどうか聞いてくれるので安全だ.ちなみにrootまたはそれに準ずる権限でvisudoコマンドを実行すること.


権限移譲とパスワードプロンプト抑制

visudoコマンド実行後,sudoersファイルが開かれる.ポインタをずっと下げていくと Allows people in group wheel to run .. というセクションが見つかる. そこらへんに以下のような行を追加.
%ADM_LIMITED ALL=(ALL) ALL
%ADM_LIMITED ALL=(ALL) NOPASSWD: ALL

これを保存すれば,全てのADM_LIMITEDユーザがパスワード入力なしで任意のコマンドをsudo経由で実行できる.sudo (ver 1.8.6p7)は毎回/etc/sudoersを読みにいくので何かのプロセスを再起動等は必要ない.ただし,新しいグループに対する変更はどうやらサーバのリブートが必要かもしれない.

個人にroot権限を付与できるが

これは出来るだけやめておこう.極私的なサーバで,一切公開の予定無しという環境であれば問題ないが,拡張性等の点からいってもベストプラクティスではないのでできるならグループ経由で権限移譲をしよう.

日曜日, 6月 02, 2013

Sambaサーバのセットアップメモ

Sambaサーバ

最近sambaサーバを再インストールするはめになったので,ついでにメモを当ブログにアップロードしておく.
ちなみにこのメモはFedora18を対象としている.他のディストリビューションとは手順4のステップが恐らくことなるだろう.

手順1. インストール

sudo yum install samba

手順2. smb.conf編集

cd /etc/samba,そしてsmb.confを編集.

workgroup = [任意のワークグループ]
netbios name = [任意の名前]
hosts allow = [アクセスを許可するホストのIPaddress, 192.168.1. で192.168.1/24ネット全体を許可 )


手順3. share directoryを作成

シェアしたいディレクトリを作成し,そのエントリをsmb.confに追加

[shareddir]
comment = shared dir
path = /home/[userdir]/shared
public = yes
writable = yes
printable = no
browsable = yes

手順4. サービスの起動及び登録

Fedora18はinit.dではなくsystemdをデフォルトでつかっているようである.
なので以下のコマンドを発行.


systemctl start smb.service
systemctl start nmb.service
systemctl enable smb.service
systemctl enable nmb.service


土曜日, 6月 01, 2013

C++ - コンソールのプログレス表示

コンソールでプログレスを表示するには

rpmのvオプションをつけた時やyumの出力等で見かける,同じラインで進捗状況を出力する機能.コマンドラインユーティリティを作る際は遅かれ早かれ必要になることも多いはず.個人的には多いのでいちおうメモということで.

ちなみにC++と題してはいるがこの方法は他の言語でも使えるはず.

コード

肝心なポイントは \r(キャリッジリターン)だけを表示すること,そしてバッファをマニュアルでフラッシュすること.少なくともgnu g++のcoutは改行をしないとバッファを標準出力にフラッシュしない.
#include <iostream>
#include <unistd.h>
#include <cstdlib>
using namespace std;

int main() {

    string progress = "progress..";
    cout << "starting.." << endl; 
    for( int i = 0;i < 10;i++ ) {

        progress += "#"; 
        cout << progress << "\r" << flush;
        sleep( 1 );
    }

    cout << endl << "done." << endl;

    return EXIT_SUCCESS;

}

日曜日, 5月 26, 2013

Fedora18にChromeをインストール

Missing Security Signatures

Googleからchromeのrpmをダウンロードしてインストールしようとしても,Fedora18ではMissing Security Signaturesというエラーが出てインストールできない.
仕方がないので以下のサイトからrpmをrpmコマンドから直にダウンロードしてインストールする.
rpm -ivh https://dl.google.com/linux/direct/google-chrome-stable_current_i386.rpm


日曜日, 5月 19, 2013

C++ - Voidポインタを活用

Voidポインタの役割

Voidポインタはあらゆる型でありえるメモリロケーションを指し示すポインタである.Genericポインタは指定された型Tのみをストアできるポインタできるという点でVoidポインタでは異なる.
Voidポインタは非常に便利だが,Voidポインタを多用しているならばそれはかなり投げやりなインターフェースデザインか,かなり極端なグランドデザインに走っていると考えたほうが良い.

つかいどころ

相当程度に万能な関数を定義する際に使うことがあるだろうか.相当緩やかな契約を提供してもよいストラテジーパターンを取るときも使ってもよいだろう.voidポインタが使われている最も有名なところはpthreadのパラメータ.

サンプル

上述したとおり,付き合い方のツボをきちんと押さえていれば,voidポインタは非常に強力なあなたの味方である.以下の例はvoidパラメータをとる異なる2つの関数をポインタ経由で呼び出している例.

#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;

void ShowIntVal(void* param);
void ShowValFromVector(void* param);

void (*pShowValFunc)(void* param);

int main() {

 // int val
 pShowValFunc = ShowIntVal;
 int val = 32;
 pShowValFunc((void*)&val);
 vector<int> v;
 v.push_back(-20);
 v.push_back(-2000);
 pShowValFunc = ShowValFromVector;
 pShowValFunc((void*)&v);

 return EXIT_SUCCESS;

}

void ShowIntVal(void* param){

 int* pIntVal = static_cast<int*>(param);
 cout << "ShowIntVal:" << *pIntVal << endl;

}

void ShowValFromVector(void* param){

 vector<int> *pV = static_cast<vector<int>*>(param);
 vector<int>::iterator iter;
 cout << "ShowValFromVector:";
 for( iter = pV->begin(); iter != pV->end();iter++)
  cout << *iter << ' ';
 cout << endl;

}



土曜日, 5月 18, 2013

C++でデザインパターン1 - シングルトン

デザインパターン

といえばやはりJava,となるのはやはりJavaが成し得たオブジェクト指向言語としての洗練さゆえか.ここではあえてC++でデザインパターンを実装していってみようと思う.

シングルトン - 適用例

最初の例はシングルトンである.ご存じの通り,あるリソースへのアクセスを制御したい時にシングルトンデザインを取る場合もある.ただし,シングルトンはユニットテストとの相性の悪さやマルチスレッド環境化において並列実行性能を低下させることもある為,敬遠されることが多い.


例の留意点

この例ではID生成シングルトンクラスを示している.この例はわかりやすさを優先しているため,実用の点から見て全くクオリファイしないことを念頭に置いてほしい.




コード

#include <iostream>
#include <ctime>

using namespace std;

class IDGenerator;

class IDGenerator {
private:
 int max;
 IDGenerator(long,int);
public:
 static IDGenerator& GetInstance(long,int);
 ~IDGenerator();
 int Next();
};

IDGenerator::IDGenerator(long seed,int maxVal):max(maxVal) {
 srand(seed);
}

IDGenerator::~IDGenerator(){}

IDGenerator& IDGenerator::GetInstance(long seed,int maxVal){
 static IDGenerator gen(seed,maxVal);
 return gen;
}

int IDGenerator::Next() {
 return rand() % max;
}

int main() {
 IDGenerator idGen = IDGenerator::GetInstance(time(NULL),100);
 for(int i = 0;i < 20;i++ ) {
  cout << idGen.Next() << " ";
 }

 return EXIT_SUCCESS;
}

木曜日, 5月 16, 2013

C++ キャスト 1 - static cast, dynamic cast そして const cast

C++ のキャスト

C++はJavaと違って様々なキャスト演算子がある.標準のキャストが以下の4つである.

  • Static cast
  • Dynamic cast
  • Const cast
  • Reinterpret cast


勿論Cスタイルのキャスト - Javaでおなじみの, カッコでくくるキャスト演算子 - もある.C言語スタイルのキャストはstatic, const, reinterpretのいずれかのキャストと同じことをするのだが,C++では使うべきではない.(こちらを参照されたし)

C++のキャストは注意深く,丁寧にC言語のキャストを機能毎に3つに分割し,かつ新たにdynamic_castを追加しているのである.4つのキャストの内,static_castは最も直観的なキャストであり,longからint, intからcharといったような型変換 かつ 変数の中身の変換を行ってくれる.
Dynamic_castはダウンキャストを行う時に,継承関係の検査を行い,正しくないキャストの場合はポインタの場合はNULL,参照の場合はstd::bad_cast例外をスローする.Reinterpret castは型だけを変換し,内部の値はタッチしない.(こちらを参照されたし).最後のconst_castはvolatileとconstを打ち消してくれるキャストである.

私のやっていることは正しいか?

Static_castはまあいいとして,他の3つのキャストを使う場面に出くわしたときは必ず以下のことを自問してほしい.

私のやっていることは,正しいのか?
このデザインは,なんか間違ってるんじゃないのか?
本当に,仕方がないのか?
これをやったら,それなりのベネフィットは,あるのか?

以上の自問について,あなたが自分と(可能ならばpeer)を納得できる答えを返すことができれば,実に結構.さもなれければリファクタリングに取り掛かったほうがよいのかもしれない.

サンプル

以下の例はstatic_cast, const_castそしてdynamic_castを使った例である.サンプルコードという手前もあって極めてナンセンスなコードであるが,上記キャストの用例としては適当かと思う.ちなみにこのコードを目の前にして上述の自問をすれば全ての問いに対して答えは,big noである.

#include <cstdio>
#include <string>
using namespace std;


class Base {
protected:
 int fieldA;
public:
 Base() { fieldA = 0;};
 virtual void ShowField(const string* prefix ) { printf("%s-%d\n", prefix->c_str(), fieldA); }
};

class Child: public Base {
public:
 Child();
 void ShowField(const string* prefix);
};

Child::Child(): Base() {
 printf("Child created.\n");
}

void Child::ShowField(const string* prefix) {
 Base::ShowField( prefix );
 string* p = const_cast<string*>(prefix);
 *p = "--the jail breaker--";

}

class Stranger {
public:
 Stranger() {}
};

int main() {

 // static_cast
 double d = 50.0;
 printf( "staticcast - %d\n", static_cast<int>(d) );

 // const_cast
 Child c;
 string str = "abc";
 c.ShowField(&str);
 printf("%s\n",str.c_str());

 // dynamic_cast
 Stranger *pS = dynamic_cast<Stranger*>(&c);
 if( pS == NULL )
  printf("downcast failed as per the pS content");

 return 0;

}


実行結果
staticcast - 50
Child created.
abc-0
--the jail breaker--
downcast failed as per the pS content


水曜日, 5月 15, 2013

STL1 .. STLコンテナとクラス

STLコンテナに自作のクラスをストアしたい

STLコンテナに自作クラスをストアさせたいことはよくあるはず.
そうしたい場合はクラスを定義する際に以下のことを留意することが大事である.

クラスコンストラクタがメモリをアロケートする必要がある場合,コピーコンストラクタと=演算子オーバーロードを定義する必要がある.

忘れてはならないのはコピーコンストラクタと=演算子のオーバーロードである.それらを忘れればたちまちデスクトラクタにおいてメモリの2重解放が発生し,プログラムはたちまちクラッシュするだろう.


コード

以下のコードはコンストラクタでメモリをアロケートしているわけではないが,コピーコンストラクタと=演算子を定義している例である.のちにクラスを拡張してポインタフィールドを足した場合でも安全である.


#include <iostream>
#include <cstdlib>
#include <vector>
#include <string>
using namespace std;

class TradeInfo 
{
protected:
 string tradeId;
 int eventId;

public:
 TradeInfo() {}
 virtual ~TradeInfo() {}
 TradeInfo(const TradeInfo& info) {
  tradeId = info.tradeId;
  eventId = info.eventId;
 }
 TradeInfo& operator =(const TradeInfo& info) {
  TradeInfo in;
  in.tradeId = info.tradeId;
  in.eventId = info.eventId;
  return in;
 }

 string GetTradeId() { return tradeId; }
 int GetEventId() { return eventId; }
 void SetTradeId(string str) { tradeId = str; }
 void SetEventid(int evt) { eventId = evt; }
 void dump() {
  cout << "TradeId-" << tradeId << ",EventId-" << eventId << endl;
 }

};

TradeInfo GenerateTradeInfo(string str,int eventId) {

 TradeInfo t;
 t.SetTradeId(str);
 t.SetEventid(eventId);
 return t;

}

int main() {

 vector<TradeInfo> v;
 v.push_back(GenerateTradeInfo("abc",1));
 TradeInfo t = v.at(0);
 t.dump();

 return EXIT_SUCCESS;

}

火曜日, 5月 14, 2013

C++ - メモリと対話する楽しみ

C++

周りの新人や,面接等をしている限りひしと感じるのは,とにかくC/C++を知っている人がどんどん減ってきていることである.私はJavaをやっていて..というのが8割方,9割,いや,もうほとんど10割である.時代の流れである.この流れをとやかく言うつもりはないし,ニーズも多いのは間違いない.なにしろC++に比べて非常に簡単に書ける.Javaならではの難しさだって多々ある.しかし,JVMの大変優れたGCや素晴らしいオープンソースライブラリのおかげでJavaの開発は他の言語,特にC/C++に比べて随分楽であることは間違いないだろう.

しかし,Javaを書いてもメモリと対話している感じはあまり感じない ( あくまでこれは私個人の一意見 ).勿論プロファイリングをしているときにヒープやpermgenの使用率をじーっとみているときはちょっとはメモリと対話してる感はあるが..C/C++の比ではない.
つまるところ,Javaは物足りなさを感じるのである.幸い私のプロジェクトはJavaとC++が入れ代わり立ち代わりなので両方を楽しむことはできているが,やはりC++を離れてJavaばかりやっているとC++に無性に戻りたくなるのである.

ということでメモリを意識できる例の一つであるC++のポリモーフィズムの仕組みを紹介する.

C++のポリモーフィズム

以下のC++のコードは実に変哲もないポリモーフィズムのコードである.が,その仕組みは関数のメモリアドレスルックアップを含む,実にC++らしいものである.

#include <iostream>
#include <iostream>
#include <cstdlib>
#include <vector>
#include <string>
using namespace std;

class Parent {

protected:
 vector<int> v;

public:
 Parent(){ cout << "constructor" << endl; }
 virtual ~Parent() { cout << "Destructor" << endl; }

 virtual void push_back(int a){ v.push_back(a); }
 //virtual void push_back(int a){ v.push_back(a); }
 virtual int get(int idx){ return v.at(idx); }

};

class Child: public Parent {

private:
 string decoration;
public:

 Child(string decorationVal="empty") { decoration = decorationVal; }

 int get(int idx){ cout << "**" << decoration << "**" << endl; return v.at(idx); }

};

int main() {

 
 Parent *p = new Child(string("takoneko"));
 p->push_back(32);
 p->push_back(16);
 cout << p->get(0) << endl;
 return 0;

}

ここではvirtual修飾子がキーである.これをつければその関数は仮想関数ということになり,その修飾子がないメソッドは非仮想関数となる. コンパイラが上記クラスをコンパイルした場合はvtableという不可視のフィールドがそのクラスに足される.そして仮想関数はvtableにエントリされる.仮想関数はvtableにエントリされない.仮想関数が呼ばれた場合は動的結合(Dynamic Binding)という仕組みで呼び出される.これはランタイム時にvtableのルックアップを行い,呼び出すべき関数のメモリアドレスを取得する.一方非仮想関数が呼ばれた場合は静的結合(Static Binding)という仕組みで呼び出される.静的結合は単純にその関数を呼び出したポインタの型であるクラスの関数を呼ぶ.このバインディングはコンパイル時に決定するので動的結合より早い.
ちなみにJavaでは全て仮想関数.protected void init()とかをJavaで書けばC++でいうところのprotected virtual void init()と同じである.

日曜日, 5月 12, 2013

Deadlock

デッドロックの定義

計算機科学の内の最重要タームの一つのデッドロック.今一度デッドロックの定義を確認しておきたい.端的に言えば,デッドロックとは

  • “二つ以上”のリソースのロックをめぐって
  • 複数のプロセスが互いに待ち合う

事象を指す.
デッドロックが発生すればデッドロック検知・処理機構が実装されていない限り,アプリケーションはクラッシュもせず処理もしない,プロセスは正常実行中のまま,という保守の観点から厄介な状態に陥る.

ここでは,デッドロックを意図的に発生させてみる.

デッドロックを意図的に発生

いざ,デッドロックを意図的に発生させるコードというのは実は思った以上に難しい.有名な4賢人の箸が最も有名だが,ここではウィキペディアのデッドロック中での原因の項で示されている例を実装してみた.

以下上記ページからの引用


ここに変数A変数Bの二つのデータと、BにAの値を加算し、Aを0にする処理AAにBの値を加算し、Bを0にする処理Bの二つの処理があったとする。マルチスレッドで処理をするため変数A変数BにアクセスするためのクリティカルセクションA(以下CSA)とクリティカルセクションB(以下CSB)の二つのクリティカルセクションを用意する。
処理Aは以下の手順であるとする。
  1. CSAに入る
  2. CSBに入る
  3. BにAの値を加算
  4. Aに0を代入
  5. CSBから出る
  6. CSAから出る
また同様に処理Bは以下の手順であるとする。
  1. CSBに入る
  2. CSAに入る
  3. AにBの値を加算
  4. Bに0を代入
  5. CSAから出る
  6. CSBから出る
この場合は以下のようにプログラムが動作するとデッドロックが発生する。

以下のコードは上記ステップをJavaで実装してみた例である.


public class Deadlock {
 
 Object critA = new Object();
 Object critB = new Object();
 int a;
 int b;
 
 public void start() throws Exception {
  
  a = 100;
  b = 50;
  Thread a = new Thread( new CriticalSectionLogic(), "A" );
  Thread b = new Thread( new CriticalSectionLogic(), "B" );
  a.start();
  Thread.sleep( 100 );
  b.start();
 }

 /**
  * @param args
  */
 public static void main(String[] args) throws Exception {
  
  Deadlock d = new Deadlock();
  d.start();

 }
 
 public void criticalSectionA( String name ) throws Exception {
  
  synchronized( critA ) {
   System.out.printf( "%s entering into CSA\n", name );
   synchronized( critB ) {
    Thread.sleep( 100 );
    b += a;
   }
   a = 0;
  
   System.out.printf( "%s exitting into CSA\n", name );
  }
  
 }
 
 public void criticalSectionB( String name ) throws Exception {
  
  synchronized( critB ) {
   System.out.printf( "%s entering into CSB\n", name );
   synchronized( critA ) {
    a += b;
   }
   b = 0;
   System.out.printf( "%s exitting into CSB\n", name );
  }
  
 }
 
 class CriticalSectionLogic implements Runnable {

  public void run() {
   
   String name = Thread.currentThread().getName();
   System.out.printf( "Thread %s starting..\n", name );
   try {
    if( name.equals( "A" ) ) {
     criticalSectionA( name );
     criticalSectionB( name );
    }
    else {
     criticalSectionB( name );
     criticalSectionA( name );
    }
   } catch ( Exception e ) {
    e.printStackTrace();
   }
    
  }

 }

}

criticalSectionAでsleep()を読んでいるのはthreadAをwaitさせる為.

実行結果は何度実行しても以下の通りとなり,このプログラムは永遠に終了しない.

Thread A starting..
A entering into CSA
Thread B starting..
A exitting into CSA
B entering into CSB
B exitting into CSB
B entering into CSA
A entering into CSB

土曜日, 5月 11, 2013

Commons Collection 4 - CompositeMap, FastList

CompositeMap - 複数のマップを統合

複数のマップをひとつのマップインスタンスからアクセスできる,ビューのようなものを利用できれば便利だなぁ,と思ったことはないだろうか.そのようなユースケースに対してはCompositeMapを使うと良い.以下のコードはマップのユースケースだが,同じような機能がセット,ひいてはコレクションに対しても用意されている ( CompositeSet, CompositeCollection ).
注意しておきたいのは,重複したキーがあった場合にはCompositeクラスは例外をスローすることである.

 @Test(expected=Exception.class)
 public void testCompositeMap() {
  
  CompositeMap compo = new CompositeMap( initMap(), initMap2() );
  assertThat( compo.size(), is( 10 ) );
  compo = new CompositeMap( initMap(), initMap() );
  
 }
 
 private Map<Integer,String> initMap() {
  Map<Integer,String> m = new HashMap<Integer,String>();
  m.put( 1, "NewYork" );
  m.put( 2, "Tokyo" );
  m.put( 3, "Bangalore" );
  m.put( 4, "London" );
  m.put( 5, "Paris" );
  return m;
 }
 
 private Map<Integer,String> initMap2() {
  Map<Integer,String> m = new HashMap<Integer,String>();
  m.put( 6, "Hongkong" );
  m.put( 7, "Seoul" );
  m.put( 8, "Cairo" );
  m.put( 9, "AllenTown" );
  m.put( 10, "GeorgeTown" );
  return m;
 }

FastList - 非同期リードを提供するリスト

Javadocにも書いてある通り,このクラスはマルチスレッド環境下で大半がリードアクセスである場合の使用を想定している.FastListを使った場合,リストへのリードアクセスは非同期化され,ライトアクセスは以下のような処理を経る.

  1. 既存のコレクションをクローン
  2. クローンに対して変更を適用
  3. 既存のコレクションをステップ2 で変更したクローンと置き換える

FastListには slow モードと fast モードの2つがあり,slowモードはリードアクセスは同期化され,ライト時のクローンも行われない.fastモードはリードは非同期,ライトもクローンプロセスを経る.
最初にこのクラスを生成したときはslowモードなので,最初の初期化が経てあとはほとんどリードオンリー,となった段階でsetFast(true)を呼ぶ必要がある.

ちなみにFast系は以下のコードで使っているFastArrayListのほかにFastHashMapとFastTreeMapがある.

コード
このコードはFastArrayListとArrayListの同じノードに対してそれぞれ100回アクセスした場合のラップタイムを計っている. 結果は何度やってもArrayListのほうが約2倍近く早い.おそらくFastArrayListは実装が色々と複雑となってしまっているのだろう.

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.apache.commons.collections.FastArrayList;
import org.apache.commons.lang3.time.StopWatch;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

public class FastListTest implements Runnable {

 List<String> list = null;

 @Before
 public void init()  {
  
  list = initFastList();

 }
 
 @Test
 public void testFastList() throws InterruptedException {
  
  ExecutorService service = Executors.newFixedThreadPool( 30 );
  StopWatch watch = new StopWatch();
  watch.start();
  for( int i = 0;i < 100;i++ ) {
   service.submit( this );
   
  }
  service.shutdown();
  service.awaitTermination( 10000L, TimeUnit.HOURS );
  System.out.println( "FAST - " + watch.getTime() );
  
  watch.reset();
  
  list = initArrayList();
  service = Executors.newFixedThreadPool( 30 );
  
  watch.start();
  for( int i = 0;i < 100;i++ ) {
   service.submit( this );
   
  }
  service.shutdown();
  service.awaitTermination( 10000L, TimeUnit.HOURS );
  System.out.println( "NONFAST - " + watch.getTime() );
  
 }

 private FastArrayList initFastList() {
  
  // slow mode first
  FastArrayList list = new FastArrayList();
  list.add( "a" );
  // switching to fast mode
  list.setFast( true );
  return list;
  
 }
 
 private List<String> initArrayList() {
  
  // slow mode first
  List<String> list = new ArrayList<String>();
  list.add( "a" );
  // switching to fast mode
  return list;
  
 }

 public void run() {
  // access to random node
  list.get( 0 );
  
 }
}

Commons Collection 3 - Buffer

Queue, Stack, Binary Heap

Bufferはスタック,キュー及びバイナリヒープの機能を提供する.
標準ライブラリにもスタック,キューはあるのでわざわざこちらを使う必要があるかどうか..の判断は私たち次第である.

コード

この例ではキューとしてUnboundedFifoBuffer, スタックとしてArrayStack, バイナリヒープとしてPriorityOrderを使っている.


import java.util.Comparator;

import org.apache.commons.collections.ArrayStack;
import org.apache.commons.collections.Buffer;
import org.apache.commons.collections.buffer.PriorityBuffer;
import org.apache.commons.collections.buffer.UnboundedFifoBuffer;
import org.junit.Test;
import static org.junit.Assert.*;
import static org.hamcrest.CoreMatchers.*;

public class CollectionsTest {

 @Test
 public void testBuffer() {
  // Queue - FIFO
  Buffer buffer = new UnboundedFifoBuffer();
  injectTestValue( buffer );
  assertThat( (String)buffer.get(), is( "1st" ) );
  assertThat( (String)buffer.remove(), is( "1st" ) );
  assertThat( (String)buffer.remove(), is( "2nd" ) );
  assertThat( (String)buffer.remove(), is( "3rd" ) );
  // Stack - LIFO
  buffer = new ArrayStack();
  injectTestValue( buffer );
  assertThat( (String)buffer.get(), is( "3rd" ) );
  assertThat( (String)buffer.remove(), is( "3rd" ) );
  assertThat( (String)buffer.remove(), is( "2nd" ) );
  assertThat( (String)buffer.remove(), is( "1st" ) );
  // BinaryHeap
  // ascending
  buffer = new PriorityBuffer( true, new StringLengthComparator<String>() );
  injectTestValueForPriorityBuffer( buffer );
  assertThat( (String)buffer.remove(), is( "/usr" ) );
  assertThat( (String)buffer.remove(), is( "/usr/local" ) );
  assertThat( (String)buffer.remove(), is( "/usr/local/bin" ) );
  // descending
  buffer = new PriorityBuffer( false, new StringLengthComparator<String>() );
  injectTestValueForPriorityBuffer( buffer );
  assertThat( (String)buffer.remove(), is( "/usr/local/bin" ) );
  assertThat( (String)buffer.remove(), is( "/usr/local" ) );
  assertThat( (String)buffer.remove(), is( "/usr" ) );
 }
 
 private void injectTestValue( Buffer buffer ) {
  buffer.add( "1st" );
  buffer.add( "2nd" );
  buffer.add( "3rd" );
 }
 
 private void injectTestValueForPriorityBuffer( Buffer buffer ) {
  buffer.add( "/usr" );
  buffer.add( "/usr/local" );
  buffer.add( "/usr/local/bin" );
 }

}

class StringLengthComparator<String> implements Comparator<String> {


 public int compare( String arg0, String arg1 ) {
  
  if( ((java.lang.String) arg0).length() > ((java.lang.String) arg1).length() )
   return 1;
  else if( ((java.lang.String) arg0).length() == ((java.lang.String) arg1).length() )
   return 0;
  else
   return -1;
  
 }
 
}


木曜日, 5月 09, 2013

Commons Collection 2 - BidiMap, Bag

BidiMap - キー又はバリューで検索可能なマップ

Commons Collectionsの本命の一つとも思われるのがこのBidiMap(Bidirectional Map)だろう.
キーのみならずバリューでも検索できるのでこれは優れものである.

以下のコードの例はキーで検索して要素を削除,そしてバリューで検索して要素を削除する例を示している.

 @Test
 public void testBidiMap() {
  
  BidiMap bidiMap = new TreeBidiMap( initMap() );
  assertThat( (String)bidiMap.remove( 4 ), is( "London" ) );
  assertThat( ((Integer)bidiMap.removeValue( "NewYork" )).intValue(), is( 1 ) );
  assertThat( bidiMap.size(), is( 3 ) );
  
 }
 
 
 private Map<Integer,String> initMap() {
  Map<Integer,String> m = new HashMap<Integer,String>();
  m.put( 1, "NewYork" );
  m.put( 2, "Tokyo" );
  m.put( 3, "Bangalore" );
  m.put( 4, "London" );
  m.put( 5, "Paris" );
  return m;
 }


バッグ - データストレージ

オブジェクトのストレージとして使えるバッグ - 使えるのか?
とりあえずサンプルコードを作ってみた.

 @Test
 public void testBag() {
  
  Bag bag = new HashBag();
  bag.add( 4, 6 );
  bag.remove( 4, 3 );
  assertThat( bag.getCount( 4 ), is( 3 ) );
  Iterator iter = bag.iterator();
  while( iter.hasNext() ) {
   System.out.println( System.identityHashCode( iter.next() ) );
  }
 }