コピーツール作った

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のクロージャでローンパターン

プログラミングで、リソースの扱いって注意を要するらしい。

  1. ファイルの開きっぱなし
  2. DBのConnectionの閉じ忘れ
  3. 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なんかでこういった仕組みを積み重ねておくとよいっすねーって話でした。

GNU IceCat から記事書きテスト!

Chromeからの記事書き込み不可の問題、いまだ放置。
なんかChromeFirefoxも同時に使うと衝突するみたいなので、更に別のブラウザ使ってみる。
実際今Ubuntuに入ってるブラウザって

  1. Google Chrome
  2. Firefox
  3. GNU IceCat
  4. Opera

と4つも入ってんなぁ。
まだソフトウェアセンターやUbuntu tweakから入れられるもの沢山あるぞ。