orange/note

バンドデータ処理プログラム

2009年9月30日更新

Nizah Blogのお題を Limboで解いてみました。

タプルと配列スライスが使えるから少し便利ですが、 それ以外は、ほとんどCになってしまいました。。

それから、ソートが言語に含まれなくて、関数ポインタを渡す方法が 分からなかったので、ソートの比較はいっぱい無理してます。 単純にYear: con 0;とかでもよかったけどね。

implement Band;

include "sys.m";
	sys: Sys;
	print: import sys;
include "draw.m";
include "string.m";
	str: String;

Band: module
{
	init: fn(ctxt: ref Draw->Context, argv: list of string);
};

Order: adt {
	pick {
	Year or Name =>
	}
};

init(nil: ref Draw->Context, nil: list of string)
{
	sys = load Sys Sys->PATH;
	str = load String String->PATH;
	name := array[] of { "The Beatles", "KRAFTWERK", "Queen", "B'z", "ThE Foo Bar" };
	year := array[] of { 60, 1970, 70, 88, 2007 };
	country := array[] of { "UK", "DE", "UK", "JP", "US" };

	# ex 1
	band := array[len name] of (string, int, string);
	for(i := 0; i < len name; i++)
		band[i] = (name[i], year[i], country[i]);

	# ex 2
	band2 := array[len band] of (string, int, string);
	band2[0:] = band;
	sort(band2, len band2, ref Order.Year);
	for(i = 0; i < len band2; i++){
		(b1, b2, b3) := band2[i];
		print("%s,%d,%s\n", b1, b2, b3);
	}

	# ex 3
	sorted_band := array[len band] of (string, int, string);
	sorted_band[0:] = band;
	sort(sorted_band, len sorted_band, ref Order.Name);

	# ex 4
	print("\n");
	for(i = 0; i < len sorted_band; i++){
		(b1, b2, b3) := correct(sorted_band[i]);
		the := "";
		if(str->tolower(b1[0:3]) == "the"){
			the = "(" + b1[0:3] + ")";
			b1 = b1[4:];
		}
		print("%-6s%-15s%d %s\n", the, b1, b2, b3);
	}
}

sort(a: array of (string, int, string), r: int, order: ref Order)
{
	sort1(a, array[len a] of (string, int, string), r, order);
}

sort1(a, b: array of (string, int, string), r: int, order: ref Order)
{
	if(r > 1){
		m := (r-1)/2 + 1;
		sort1(a[0:m], b[0:m], m, order);
		sort1(a[m:r], b[m:r], r-m, order);
		b[0:] = a[0:r];
		for((i, j, k) := (0, m, 0); i < m && j < r; k++){
			cmp: int;
			pick c := order {
			Year =>
				cmp = yearcmp(b[i], b[j]);
			Name =>
				cmp = namecmp(b[i], b[j]);
			* =>
				raise "fail:order";
			}
			if(cmp > 0)
				a[k] = b[j++];
			else
				a[k] = b[i++];
		}
		if(i < m)
			a[k:] = b[i:m];
		else if(j < r)
			a[k:] = b[j:r];
	}
}

yearcmp(b1: (string, int, string), b2: (string, int, string)): int
{
	(nil, y1, c1) := correct(b1);
	(nil, y2, c2) := correct(b2);
	if(y1 > y2)
		return 1;
	else if(y1 < y2)
		return -1;

	if(c1 > c2)
		return 1;
	else if(c1 == c2)
		return 0;
	else
		return -1;
}

namecmp(b1: (string, int, string), b2: (string, int, string)): int
{
	name1 := namei(b1);
	name2 := namei(b2);
	if(name1 > name2)
		return 1;
	else if(name1 == name2)
		return 0;
	else
		return -1;
}

correct(band: (string, int, string)): (string, int, string)
{
	(name, year, country) := band;
	if(year < 100)
		year += 1900;
	return (name, year, country);
}

namei(band: (string, int, string)): string
{
	(name, nil, nil) := band;
	name = str->tolower(name);
	if(name[0:3] == "the")
		name = name[4:];
	return name;
}

2文字以下のバンド名("X"など)が初期データにあると落ちる。 theの判定のところでname[0:3]が配列境界を越えるから。

2009年9月30日追記

関数ポインタは、ref fn()のように書くと渡せる。

implement Fn;

include "sys.m";
	sys: Sys;
include "draw.m";

Fn: module
{
	init: fn(ctxt: ref Draw->Context, argv: list of string);
};

Int: adt
{
	n: int;
};

init(nil: ref Draw->Context, nil: list of string)
{
	sys = load Sys Sys->PATH;

	A1 := array[] of { "a1", "b2", "c3" };
	A2 := array[] of { ref Int(1), ref Int(2), ref Int(3) };

	apply(A1, printstring);
	apply(A2, printint);
}

apply[T](a: array of T, f: ref fn(p: T))
{
	for(i := 0; i < len a; i++)
		f(a[i]);
}

printint(n: ref Int)
{
	sys->print("%d\n", n.n);
}

printstring(s: string)
{
	sys->print("%s\n", s);
}

qsortの比較関数プロトタイプなど、Cでvoid*を使う状況では、 パラメトリック多相型というものが同じような目的に使える。 上記の例でいえば、apply関数で使っているT型。 これは、ref型しか扱えない(型の解決ができない)という制限があるので、 intを直接扱うといったことはできない。