照度分布計算その8 - 出力のFormat [回折による照度分布計算]
回折計算の実装。前書いたデバグ用のMathematicaのフォーマットの出力。デバグだけではなくて結果の出力にも使うのでまとめておく。
入出力のフロントエンドにとりあえず最初はMathematicaを使うことに決めた。それにあわせて、出力はMathematicaの式(Expression)の形の文字列で書き出すためのプロトコルを作っておく。
@protocol SMathematicaStyleDescription - (NSString *)mathematicaStyleDescription; @end @interface NSArray (SMathemticaStyle) <SMathematicaStyleDescription> @end @interface NSDictionary (SMathemticaStyle) <SMathematicaStyleDescription> @end @interface NSString (SMathemticaStyle) <SMathematicaStyleDescription> @end @interface NSNumber (SMathemticaStyle) <SMathematicaStyleDescription> @endのように普通に使うクラスにもカテゴリでを使って同じプロトコルを強制しておく。例えばNSArrayは
@implementation NSArray (SMathemticaStyle) - (NSString *)mathematicaStyleDescription { NSMutableString *ret = [NSMutableString stringWithString:@"{"]; NSEnumerator *en = [self objectEnumerator]; id obj; while ((obj = [en nextObject]) != nil) [ret appendFormat:@"%@, ", [obj mathematicaStyleDescription]]; [ret appendString:@"Sequence[]}\n"]; return ret; } @endのように中身をMathematicaのListの形式で出力する。Objective-C2.0のfast enumerationの書き方の方が簡単なのにいまだに古いまま書いてるな。あとも同じでNSDictionaryのほうも
@implementation NSDictionary (SMathemticaStyle) - (NSString *)mathematicaStyleDescription { NSMutableString *ret = [NSMutableString stringWithString:@"{"]; NSEnumerator *en = [self keyEnumerator]; id key; while ((key = [en nextObject]) != nil) { id obj = [self objectForKey:key]; [ret appendFormat:@"%@ -> %@, ", key, [obj mathematicaStyleDescription]]; } [ret appendString:@"Sequence[]}\n"]; return ret; } @endのようにMathematicaのRule(a->b)の形で出力する。ついでに残りも書いてしまうと
@implementation NSString (SMathemticaStyle) - (NSString *)mathematicaStyleDescription { return self; } @end @implementation NSNumber (SMathemticaStyle) - (NSString *)mathematicaStyleDescription { return [self stringValue]; } @endなんていうもの。これは-descriptionを使うのと同じ。
で、例の複素2次元配列のクラスにも
@implementation SAmplitudePlane - (NSString *)mathematicaStyleDescription { NSMutableString *ret = [NSMutableString stringWithFormat:@"{"]; int h, v; complex *cp = cPlane; for (v = 0 ; v < sSize ; v ++) { [ret appendString:@"{"]; for (h = 0 ; h < sSize ; h ++) { [ret appendFormat:@"%10.8f %+10.8fI, ", cp->re, cp->im]; cp ++; } [ret appendString:@"Sequence[]},\n"]; } [ret appendString:@"Sequence[]}\n"]; return ret; } @endとしておく。これも単に複素数のListのListとして返すだけ。一つの要素は
[ret appendFormat:@"%10.8f %+10.8fI, ", cp->re, cp->im];で書いている。NSStringのformatはANSI-Cのprintfのformatをそのまま使うことができる。MathematicaはFORTRANの000e±00型の表現は直接は読めなくて、ちゃんと数学的に正しい000*10^±00でないとだめ(Mathematicaの文法ではaaa e±bbは十進数aaaとシンボルのeとの積に十進数bbを足すか引くかするという意味に解釈される。eを大文字にしてaaa E±bbとするとEは自然対数の底として解釈されてしまう。積の演算子「×」を落としていいという文法にしたトレードオフ)。外部のファイルを読むためのReadListなどでは読み込み関数内部でこの形の実数を変換できるけど、逆に複雑な構造は表現できなくなる。せいぜいい2次元配列ぐらいまで。どのみち今回の計算では、指数を持つような値は精度の範囲外なので指数を使わない形で書き出すことにする。
format stringの%の後ろの+符号は、正の数にも符号を付けるという意味になるので、Mathematicaで読んだときはそれが演算子として解釈されるので都合がいい。これは
[ret appendFormat:@"Complex[%10.8f %,10.8f], ", cp->re, cp->im];としてもMathematicaに読んでしまえば同じになる。こっちの方が読みやすいけど出力ファイルは"Complex"という文字の数だけ大きくなってしまう。ただせさえ大きなファイル(runThrough[]を使った場合でもpipeのスループットに影響する)になるので、さらに3割大きくなってしまうのは結構痛い。
2008-06-20 23:00
nice!(0)
コメント(0)
トラックバック(0)
コメント 0