2005年03月01日

Setクラス


集合を扱うSetクラスを書いてみました。
まだ、十分なテストはしてませんが、基本的な部分は問題なく動くと思います。
演算子 + , - & ^ をオーバーロードしています。

using System;
using System.Collections;

namespace Gushwell.Library {
public interface ISet : ICollection{
bool IsEmpty { get; }
bool Contains(ISet set);
bool Contains(object o);
bool Add(object o);
bool Add(ISet col);
void Remove(object o);
void Remove(ISet set);
void Clear();
object[] ToArray();
ISet Union(ISet set);
ISet Except(ISet set);
ISet Intersect(ISet set);
ISet Xor(ISet set);

}

public class HashSet : ISet, ICloneable {
private Hashtable hash;

public HashSet() {
hash = new Hashtable(16);
}

public HashSet(int capacity) {
hash = new Hashtable(capacity);
}

public HashSet(ISet set) {
hash = new Hashtable(set.Count);
this.Add(set);
}

#region ISet メンバ

public virtual bool IsEmpty {
get { return Count == 0; }
}

public virtual bool Contains(ISet set) {
foreach ( object o in set ) {
if ( !hash.ContainsKey(o) )
return false;
}
return true;
}

public virtual bool Contains(object o) {
return hash.Contains(o);
}

public virtual bool Add(object o) {
if ( !hash.ContainsKey(o) ) {
hash.Add(o,null);
return true;
}
return false;
}

// 一つでも登録できれば true;
public virtual bool Add(ISet col) {
bool result = false;
foreach ( object o in col ) {
if ( Add(o) ) {
result = true;
}
}
return result;
}

public virtual void Remove(object o) {
hash.Remove(o);
}

public virtual void Remove(ISet set) {
foreach ( object o in set ) {
hash.Remove(o);
}
}

public virtual void Clear() {
hash.Clear();
}

public object[] ToArray() {
object[] objs = new object[Count];
int i = 0;
foreach ( object o in this ) {
objs[i] = o;
i++;
}
return objs;
}


public virtual ISet Union(ISet set) {
HashSet hs = new HashSet(this);
hs.Add(set);
return hs;
}

public virtual ISet Except(ISet set) {
HashSet hs = new HashSet(this);
hs.Remove(set);
return hs;
}

public virtual ISet Intersect(ISet set) {
HashSet hs = new HashSet(this);
hs.Remove(set);
HashSet hs2 = new HashSet(this);
hs2.Remove(hs);
return hs2;
}

public virtual ISet Xor(ISet set) {
HashSet union = (HashSet)(this.Union(set));
HashSet and = (HashSet)(this.Intersect(set));
return union.Except(and);
}

#endregion

#region ICollection メンバ

public virtual void CopyTo(Array array, int index) {
foreach ( object o in array )
hash.Add(o,null);
}

public virtual Object SyncRoot {
get { return hash.SyncRoot; }
}

public virtual bool IsSynchronized {
get { return false; }
}

public virtual int Count {
get { return hash.Count; }
}


#endregion

#region IEnumerable メンバ

public virtual IEnumerator GetEnumerator() {
return hash.Keys.GetEnumerator();
}


#endregion

#region ICloneable メンバ

public object Clone() {
HashSet hs = new HashSet(this.Count);
hs.Add(this);
return hs;
}

#endregion

public override string ToString() {
string s = "[";
bool first = true;
IEnumerator en = this.GetEnumerator();
while ( en.MoveNext() ) {
if ( !first ) {
s += ", ";
}
first = false;
s += en.Current.ToString();
}
s += "]";
return s;
}

public static ISet operator +(HashSet a, HashSet b) {
return a.Union(b);
}

public static ISet operator -(HashSet a, HashSet b) {
return a.Except(b);
}

public static ISet operator &(HashSet a, HashSet b) {
return a.Intersect(b);
}

public static ISet operator ^(HashSet a, HashSet b) {
return a.Xor(b);
}
}
}
  

Posted by gushwell at 20:29Comments(0)TrackBack(0)C#

2005年02月24日

Invokeメソッド


リフレクションのInvokeメソッドを使うとき、パラメータの与え方でバカをやってしまいました。
MyClassにあるstaticメソッドのMaxが以下のように定義されていた時に、
   public static double Max(double[] nums);
本来は、
  Type type = Type.GetType("MyClass");
MethodInfo minfo = type.GetMethod("Max");
double[] obj = new double[] { 1, 2, 3, 4, 5, 8, 2 };
object[] prm = new object[1] { obj };
double ans = (double)(minfo.Invoke(null,prm));
と書くべきところを
  Type type = Type.GetType("MyClass");
MethodInfo minfo = type.GetMethod("Max");
double[] obj = new double[] { 1, 2, 3, 4, 5, 8, 2 };
double ans = (double)(minfo.Invoke(null,prm));
と書き、なぜ、例外が発生するのか悩んでしまいました。
これだと、7つの引数がある、Max を呼び出していることになりますが、それに気が付きませんでした。

トイレに行くのに席を立ち、廊下を歩いているときに、「あっ、そうか」と、間違った理由がわかりました。
結構、一息入れた時に間違いの原因に気が付くことってありますよね。  
Posted by gushwell at 20:50Comments(0)TrackBack(0)リフレクション

2005年02月23日

== 演算子のオーバーロード


== 演算子をオーバーロードしたら、

if ( data == null )

という箇所で、エラーとなりました。ちょっと考えれば、あたり前のことなのですが、一瞬「なぜ」と思ってしまいました。
.NET Framework のドキュメントを読むと、参照型でのオーバーロードはやらないほうが良い、というニュアンスのことが書かれていますし、やはり、参照型に対して、== をオーバーロードしないほうが良いのかな。

確かに、参照型で、== をオーバーロードしてしまうと、同一インスタンスを指しているかを調べられなくなりますからね。

まあ、このクラスは、もともと値型(構造体)の方がふさわしいのですが、Boxing,UnBoxing されるのがイヤだったので、参照型(クラス)で定義しました。
NullObject を導入して、

if ( data is MyNullData )

と書くようにしようかとも思ったのですが、うーーん、やっぱり、== はオーバーロードしないほうが良さそうです。ということで、== をオーバーロードするのは止めました。
  
Posted by gushwell at 20:07Comments(0)TrackBack(0)C#

2005年02月22日

ミニ言語(2)


ミニ言語がなんとか動き始めました。ちょっと感動ものですね。構文ツリーをVisitorパターンを使ってたどりながら、実行するインタープリターです。P−CODEのようなオブジェクトを吐き出し、それを実行するインタープリタにしたほうが、実行速度は速くなると思われたのですが、処理速度が命の言語ではないので、パーザーが吐き出した構文ツリーをたどりながら実行する方式としました。
なお、この方式の利点は、演算子の優先順位を構文ツリーの中に埋め込めることです。BNFを書くときに優先順位を考慮した構文にしておくのですが、こうすれば、演算子の優先順位を意識することなく、構文ツリーをたどるだけで、計算が出来てしまいます。
なかなか言葉だけでは表現が難しいですね。

とにかく、デザインパターンを適用することで、思いのほか短時間で動くようになりました。
State,Compsite,Interpreter, Visiter, Iterator ,Singleton, NullObjectといったパターンを使っています。
このようなパターンを知らなかったら、きっと数倍の時間がかかっていたと思います。まだまだ完成にはほど遠いですが、先が見えました。  
Posted by gushwell at 22:15Comments(0)TrackBack(0)C#

2005年02月17日

ミニ言語


C#でミニ言語(インタプリタ)を実装しようとしています。ミニ言語といえば、ニクラス・ヴィルトのPL/0が有名ですね。PL/0をgoogleで検索し、参考になりそうなWebページがないか調べてみました。ソースコードを公開されている方もいたようですが、さすがにC#版はなさそうです。ということで、すべて自作するという道を選びました。
まずはInterpreterパターンを使って、構文解析するところを作成。代入文、if文、while文、関数くらいしか無い、本当に小さな言語ですが、思いのほか Expression 部分が複雑になりそうです。BNF を書いたらこの部分が結構な分量でした。まあ、BNFがかければ後は、機械的にクラスわけしてコーディングするだけです。
実行部分は、Commandパターンと、この前勉強した Visitorパターンと が使えそうです。
  
Posted by gushwell at 22:35Comments(0)TrackBack(0)C#

2005年02月16日

NullObject


NullObject を導入したら、
while ( (tok = lex.NextToken()) != null ) {
...
}

という箇所が
while ( !((tok = lex.NextToken()) is NullToken) ) {
...
}
となってしまった。うーーん、読み難いですね。
「それは、IEnumerable を実装して、foreach を使えるようにすれば、
いいんじゃないの」と誰かから言われそうですが、今は面倒なのでパス。
while ( (tok = lex.NextToken()) isnot NullToken ) {
...
}
とでも書ければ良いのだけれど...
  
Posted by gushwell at 22:15Comments(0)TrackBack(0)パターン