1つのコードでプログラミング言語を理解する。

飛行機の中でActionScript Cookbook の続きを読んでたわけですが

ActionScript 本 2冊 (Nizah blog)

こないだ思ったのは プログラミング初学者はともかく、3つや4つの言語が使えるようになってくると 共通部分なんか要らなくて、その言語の特徴だけ掴めれば良いよね。 Cookbook ってその目的に合ってるんじゃない?

今日は、更にそれを進めて

ある1つのプログラムの中に、プログラミング言語 foo のエッセンスが上手く詰まってて、それを理解すればとりあえずサクサク使えるようなプログラム

を、作る事は出来ないだろうか、と。 多分、出来るよね。 Perl ならこのコードで一発、 Ruby ならこのコードで一発みたいな。

更に、それを進めて、言語が変わってもやっぱり上手い事エッセンスが詰まってるような、そういうお題は出来ないだろうか、と。

とりあえず、"Hello, world!" が目的のコードでは無い事は確かだ。 それだけじゃ何も分からん。

要りそうなのは ・簡単なデータ構造と、その処理(配列なめるとか) ・データ(文字列/数値)の比較、ソート ・文字列の処理(正規表現、あるいはそれに準じた) ・関数/メソッド の定義と呼び出し、(コールバックや無名関数……は高級すぎるかな?) ・出力のフォーマット

あまり要らないのは ・基本的な制御構造(変わっているものは使おう) ・自作クラスやモジュール(それがその言語で高度な使い方にあたるならば、当面は要らない)

ってんで、とりあえずゆるーく考えてみた。


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

初期データは、3種類のデータがバラバラに与えられます。 (面倒なのでファイル読み込みとかは要らない、忠実にこのデータをセットしよう)

name: The Beatles, KRAFTWERK, Queen, B'z, ThE Foo Bar

year: 60, 1970, 70, 88, 2007

country: UK DE UK JP US

それぞれ、バンド名、結成年、国です。 とある理由により、結成年は4桁と2桁が混ざっています。

ステップ 1 3つのデータを、1つのデータ構造体 (band) にまとめて保存しなさい。 ただし、順序も保存する事(元の並びと同じように) それぞれの値も、元の値をそのまま保存するように(60 → 1960 と変換して扱ったりしちゃダメ)

ステップ 2 各バンドの情報を、結成年で並べ替えて出力します。 もし、結成年が同じ場合は、国(昇順)で判断します。 もちろん、2桁表記も4桁表記も、正しく解釈して古い順に並べなさい。 表示は1行1バンドで、バンド名、結成年、国をカンマ区切りで出力します。 表示する結成年は元の表記に忠実に。 band データ構造体自体を変更してはいけません。

ステップ3 各バンドの情報を、名前で並び替えた新しいデータ構造体 sorted_band を作りなさい。 名前はcase-insensitive(小文字大文字無視)で 昇順で並び替えますが、先頭の The (これも小文字大文字関係なく) は無視して並び替えます。 The Beatlesは、SとUの間ではなく、AとCの間に来るように。 もちろん、sorted_band の各データは band と同じように、Theが外れた名前等を保存してはいけない。

ステップ4 sorted_band のデータを出力します(その前に空行を1つ出そうか) バンド名が The (case-insensitive) で始まる場合は、丸括弧で囲み、更にTheがあるものと無いものが同じカラム位置で揃うようにします。 結成年は 21文字目の位置で揃え、国は26文字目の位置(年4桁+スペース1つ)で揃えます。

期待される出力

The Beatles,60,UK
KRAFTWERK,1970,DE
Queen,70,UK
B'z,88,JP
ThE Foo Bar,2007,US
B'z           1988 JP
(The) Beatles       1960 UK
(ThE) Foo Bar       2007 US
KRAFTWERK     1970 DE
Queen         1970 UK


こんな感じでどうかな、大きなプログラムじゃないけど、結構色々やらなきゃいけない。

Perl で書いてみた。

band_code.pl

use strict;
my @name = ("The Beatles", "KRAFTWERK", "Queen", "B'z", "ThE Foo Bar");
my @year = (60, 1970, 70, 88, 2007);
my @country = qw(UK DE UK JP US);
my @band;
for(my $i=0; $i<@name; $i++){
push(@band, {   name => $name[$i],
year => $year[$i],
country => $country[$i] } );
}
foreach my $in (sort { year_format($a->{year}) <=> year_format($b->{year})
or $a->{country} cmp $b->{country} } @band){
print join(",", $in->{name}, $in->{year}, $in->{country}),"\n";
}
print "\n";
my @sorted_band = sort by_name @band;
foreach my $in (@sorted_band){
print_band($in);
}
sub year_format {
my $year = shift;
$year += 1900 if $year < 100;
return $year;
}
sub by_name {
my $a_name = $a->{name};
my $b_name = $b->{name};
$a_name =~ s/^the //i;
$b_name =~ s/^the //i;
uc($a_name) cmp uc($b_name);
}
sub print_band {
my $ref = shift;
my $length;
my $name = $ref->{name};
my $year = $ref->{year};
my $country = $ref->{country};
if($name =~ s/^(the) /($1) /i){
print $name;
$length += length($name);
}else{
print " " x 6, $name;
$length += length($name)+6;
}
print " " x (20 - $length);
print year_format($year);
print " $country\n";
}

もっと入れなきゃいけない要素はあるかな? 短くすれば良いってもんじゃないし、冗長に書いてもC風言語にしかならん。 他の言語は使える人が、想像で補完出来る程度の難解さと、これは便利だなと思える程度の技を入れてPerlで書くとこんな感じかなぁ # あー、コメントは入れなきゃいかんよね、コメントは。

このお題で、他の言語もいけるかな? 誰か他の言語でやってみたり、もっと良いネタ考えてくれんかな

初期データは、もっといやらしい感じにしなきゃいけないんだけど、考えるのが面倒だった