コピーツール作った
2日でとりあえずベータ版できた。
事前調査はしたけど、こんなん既に有ったら悲しいなw
自己要件下記。
1.バッファをメモリとファイル(任意のパス)から選んで設定できる
→RAMDisk等の高速なストレージを有効活用する
2.バッファのサイズを設定できる
→大容量メモリ等に対応
3.一つのファイルを読み込んで、同時に複数のコピー先へ書き込みできる
→バッファへの読み込みのコストを最小にする
http://www8382u.sakura.ne.jp/code/goodcopy-0.1.src.zip
usage:メモリバッファ
goodcopy -m -s 512m source target1 target2 target3
usage:ファイルバッファ(未実装)
goodcopy -f /pass/to/buffer.buf -s 2g source target1 target2 target3
C++でアクターモデルの実装
Scalaで便利なアクターをC++でも使えないかと、簡単な実装を行なってみた。
アクターはキューを持たず、メッセージを受付けた時にスケジューラにスレッドによる実行をキューイングします。
スレッドはスケジューラが管理していて、キューからアクターとメッセージを取り出してメッセージ処理を実行します。
メッセージはMessageクラスのサブクラスで作成し、メッセージ処理時にtypeidでキャストして処理します。
typeidで正しくサブクラスにキャストできるように、Messageの持つ仮想関数をオーバーライドします。
あ、まずい。
これだとActorが自身へのメッセージを随時実行する事が保証されなくなるぞ…。
スレッドセーフ確保の為にこれ作ったのに…。
まあそれも確かアクターモデルでは許されてた気もするが…。
ちょっと影響度を考えておかないと…。
Actor.h
#ifndef ACTOR_20110913 #define ACTOR_20110913 #include <typeinfo> #include "Scheduler.h" #include "Message.h" class Message; class Actor { protected: Scheduler* scheduler; public: Actor(Scheduler*); virtual void send(Message*) = 0; virtual void execute(Message*) = 0; }; class TestActor : public Actor { public: TestActor(Scheduler*); void send(Message*); void execute(Message*); }; #endif
Actor.cpp
#include "Actor.h" Actor::Actor(Scheduler* sch):scheduler(sch){} TestActor::TestActor(Scheduler* sch):Actor(sch){} void TestActor::send(Message* message) { scheduler->entry(this,message); } void TestActor::execute(Message* message) { const std::type_info& info = typeid(*message); if(info == typeid(TestMessage)){ TestMessage* testmes = (TestMessage*)message; testmes->display(); } else { std::cout << "TestActor:CastError" << std::endl; std::cout << "CurrentMessage:" << info.name() << std::endl; } delete message; }
Scheduler.h
#ifndef SCHEDULER_20110913 #define SCHEDULER_20110913 #include <queue> #include <pthread.h> #include <semaphore.h> #include <utility> // for pair #include "Actor.h" class Actor; #include "Message.h" class Message; class Scheduler { private: static const int THREADS_NUM = 100; std::queue<std::pair<Actor*,Message*> > entries; pthread_mutex_t entriesMutex; sem_t sem; pthread_t threads[THREADS_NUM]; static void* threadEntry(void*); public: Scheduler(); void entry(Actor*,Message*); }; #endif
Scheduler.cpp
#include "Scheduler.h" Scheduler::Scheduler() { pthread_mutex_init(&entriesMutex,NULL); sem_init(&sem,0,0); for(int i = 0; i < THREADS_NUM; i++) { pthread_create(&threads[i],NULL,&threadEntry,(void*)this); } } void* Scheduler::threadEntry(void* arg) { Scheduler* sch = (Scheduler*)arg; while(true) { sem_wait(&(sch->sem)); pthread_mutex_lock(&(sch->entriesMutex)); std::pair<Actor*,Message*> pair = sch->entries.front(); sch->entries.pop(); pthread_mutex_unlock(&(sch->entriesMutex)); pair.first->execute(pair.second); } } void Scheduler::entry(Actor* actor,Message* message) { pthread_mutex_lock(&entriesMutex); entries.push(std::pair<Actor*,Message*>(actor,message)); sem_post(&sem); pthread_mutex_unlock(&entriesMutex); }
Message.h
#ifndef MESSAGE_20110913 #define MESSAGE_20110913 #include "Actor.h" #include <string> class Actor; class Message { protected: virtual void share(){} }; class TestMessage : public Message { protected: virtual void share(){} private: std::string mes; public: TestMessage(std::string str); void display(); }; #endif
Message.cpp
#include "Message.h" #include <iostream> TestMessage::TestMessage(std::string str):mes(str){} void TestMessage::display() { std::cout << "TestMessage was called!:" << mes << std::endl; }
test.cpp
#include "Message.cpp" #include "Actor.cpp" #include "Scheduler.cpp" #include <list> int main() { Scheduler sch; std::list<TestActor*> actors; std::list<TestMessage*> mes; for(int i=0; i<1000; i++) { actors.push_front(new TestActor(&sch)); mes.push_front(new TestMessage("HelloBoy")); } std::list<TestActor*>::iterator ite_a = actors.begin(); std::list<TestMessage*>::iterator ite_m = mes.begin(); while(ite_a != actors.end() && ite_m != mes.end()) { (*ite_a)->send(*ite_m); ite_a++; ite_m++; } while(true){} return 0; }
containsAll,containsOne - 文字列に全て含む,少なくとも一つ含む
package toming { object MyRichString { implicit def stringToMyRichString(str:String) = new MyRichString(str) } class MyRichString(string:String) { private[this] def applyAll (conditions:String*)(bool:Boolean) = { conditions.map(string.contains(_)).contains(bool) } //String must clear all conditions. //i:ignore cases def containsAll(conds:String*) = { ! applyAll(conds:_*)(false) } //String must clear at least one. def containsOne(conds:String*) = { applyAll(conds:_*)(true) } } }
データと繋ぐペアのSetから一連のチェーン作成
case class Node(path:String,rev:Int) object NullNode extends Node(null,-1) case class Joint(before:Node,after:Node) val set = Set[Joint]( Joint(NullNode,Node("a",1)), Joint(Node("a",1),Node("d",4)), Joint(Node("a",1),Node("b",2)), Joint(Node("b",2),Node("b",3)), Joint(Node("b",2),Node("c",4)) ) def chain(set:Set[Joint],joint:Joint):List[List[Node]] = { println(joint) val children = set.filter(_.before==joint.after) children.size match { case 0 => List(List(joint.after)) case n => children.map(chain(set,_)). foldLeft(List[List[Node]]())(_++_). map( joint.after :: _ ) } } chain(set,Joint(NullNode,Node("a",1)))
REPLでこちらを投げると、
List( List(Node(a,1), Node(b,2), Node(c,4)), List(Node(a,1), Node(b,2), Node(b,3)), List(Node(a,1), Node(d,4)) )
これが得られます。
まあ多分collection.immtable.TreeSetでうまいことできるんだろうがなぁ。
まあ車輪の再発明でも、もうちょっと抽象化して汎用性を高めたいとこ。
Nodeと定義したとこをなんか型パラメータで定義したりして。
入力されたキーのキーコードを表示するAWTアプリ
キーコードをじゃんじゃん調べるために作成。
mainを持つクラスはなし。
"new KeyCodeFinder"で始動。
import java.awt._ import java.awt.event._ class KeyCodeFinder extends Frame { this setTitle "KeyCodeFinder - Focus Me And Push Keys." this setFocusTraversalKeysEnabled false val label = new Label label setAlignment 1 this addKeyListener (new KeyCodeListener(this)) this addWindowListener (new KeyWindowListener(this)) this add label setSize(360,240) setVisible(true) } class KeyCodeListener(finder:KeyCodeFinder) extends KeyAdapter { override def keyPressed (e:KeyEvent) { val label = finder.label val text = e.getKeyCode.toString val fontsize = ( finder.getWidth / text.length ) min finder.getHeight val font = new Font("Monospaced",0,fontsize) label setFont font label setText text } } class KeyWindowListener(finder:KeyCodeFinder) extends WindowAdapter { override def windowClosing(e:WindowEvent) { finder.dispose } }
scalaのクロージャでローンパターン
プログラミングで、リソースの扱いって注意を要するらしい。
- ファイルの開きっぱなし
- DBのConnectionの閉じ忘れ
- DBのStatementの閉じ忘れ
とかとか?
これらのリソースを扱う全ての場所で、
ちゃんと「close」のメソッドを呼びださなきゃならないって面倒よね。
全部のソース自分で書く訳じゃない時とか特にそう。
そこでscalaのクロージャを使ったローンパターン。
trait HasConnections { import java.sql._ //実装ヨロ(DBMSの種類によって作り分けが可能) val connection:Connection //ローンパターン(使ったコネクションの後処理をこちらで管理できる) //この実装の場合はコネクションの数は単一 protected def use[T](procedure:Connection=>T):T = procedure(connection) //インスタンス消滅の際にきっちりクローズ処理を実行 //この実装方法でいいのかしら・・・。 protected override def finalize = { connection.close super.finalize } } abstract class DataAccessor extends HasConnections { import java.sql._ //ResultSetを返すSQL文(select文のみ?)専用 //ローンパターン。 def query[T](sql:String)(procedure:ResultSet=>T):T = { //connectionはuseメソッドが持ってきてくれる //connectionの後処理は考えなくて良い use { connection => val statement = connection.createStatement val resultset = statement.executeQuery(sql) val result = procedure(resultset) resultset.close //←明示的にcloseする必要はない??? statement.close //自分で準備したものは自分で片付ける result } } def update(sql:String):Int = { use { connection => val statement = connection.createStatement val result = statement.executeUpdate(sql) statement.close result } } } //Apache Derby用の具象クラス class DerbyAccessor extends DataAccessor { //抽象定義だけであったconnectionを定義 val connection = { val driver = new org.apache.derby.jdbc.AutoloadedDriver driver.connect( "jdbc:derby:mydb;create=true", new java.util.Properties ) } }
これで、DerbyAccessorのインスタンスを作成して、mydbにアクセスできます。
ローンパターンの活かされた使用側の用法は下記のようになります。
$ scala -cp ./:./derby-10.7.1.1.jar Welcome to Scala version 2.9.0.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_24). Type in expressions to have them evaluated. Type :help for more information. scala> new DerbyAccessor res0: DerbyAccessor = DerbyAccessor@3aa450bb scala> res0.update("create table hoge ( id integer,name varchar(256) )") res1: Int = 0 scala> res0.update("insert into hoge values ( 1, 'tanaka' )") res2: Int = 1 //******************* レコード追加省略 **************************// scala> res0.update("insert into hoge values ( 6, 'ueda' )") res7: Int = 1 //次の行注目!! scala> res0.query("select id, name from hoge"){ res => while(res.next) println(res.getInt(1)+"\t"+res.getString(2)) } 1 tanaka 2 sato 3 saito 4 suzuki 5 honda 6 ueda scala>
最後の入力でやってるように、queryメソッドに渡している引数は2つ。
- SQLのクエリ(select文などResultSetが帰ってくるもの)
- ResultSetを引数にとり、任意の型を返す関数
- 「ResultSetに対してこれこれをこうしろ」というブロック
て感じで使用できる。
Connectionの存在も、Statementのcreateもcloseも意識する必要はない。
でもまあRubyなどでも同じ仕組み余裕でありますね。
#ファイルに対して行う処理を書く。後処理はやってくれる。 open("filename.txt","w") do |file| file.puts("Hello, Ruby!") end #リソース関係ないけど、各ファイルパス全てに対して行う処理を定義できる。 Find.find("./") do |path| puts path if File.file?(path) end
てか、関数に関数を渡せる事でできることって相当広がりますな。
最初にRubyで感動したのはソート。『何を基準に順序付けするか』を定義できるのがいい。
もちろんScalaでも同じことができます。
説明は省略。
うまく呼び出し側がピリピリしなくても問題の発生しにくいコーディングができるように、
traitなんかでこういった仕組みを積み重ねておくとよいっすねーって話でした。