シュワルツ変換(シュウォーツ変換)

ウチの この一行は絶対無二の一行なり の naruse さん(成瀬ゆい さん?)の書き込み経由で読んだ、「Perlメモ#特定の項目でソートする」の Perl スクリプトの意味が分からなかったので Smalltalk に(ほぼ)直訳になるように書き直して理解をこころみる。

中断して久しい“「結城浩の『Perl クイズ』」を Smalltalk/Squeak で”の番外編(いや、再開のためのリハビリ…)的に。w

低速版

Perl

@data = ('A,2,黄緑',
         'C,3,青紫',
         'B,4,赤',
         'C,6,青',
         'A,7,緑',
         'A,9,紫',
         'B,10,黄');
@data = sort {
  my ($alpha_a, $num_a, $color_a) = split(/,/, $a);
  my ($alpha_b, $num_b, $color_b) = split(/,/, $b);
  $num_a <=> $num_b;
} @data;


Smalltalk

data
data _ #('A,7,緑' 'C,6,青' 'B,4,赤' 'A,9,紫' 'A,2,黄緑' 'B,10,黄' 'C,3,青紫'). data _ data sort: [: a : b | | numA numB | numA _ (a subStrings: #($,)) second asNumber. numB _ (b subStrings: #($,)) second asNumber. numA < numB]

シュワルツ変換

Perl

@data = map {$_->[0]}
            sort {$a->[2] <=> $b->[2]}
                 map {[$_, split /,/]} @data;


Smalltalk

data _ ((data collect: [: str | str subStrings: #($,)]) 
   sort: [: a : b | a second asNumber < b second asNumber]) 
      collect: [: ary | String streamContents: [: ss | 
         ary do: [: elem | ss nextPutAll: elem] separatedBy: [ss nextPut: $,]]]

高速版

Perl

@tmp = map {(split /,/)[1]} @data;
@data = @data[sort {$tmp[$a] <=> $tmp[$b]} 0 .. $#tmp];

Smalltalk

data temp
data _ #('A,7,緑' 'C,6,青' 'B,4,赤' 'A,9,紫' 'A,2,黄緑' 'B,10,黄' 'C,3,青紫'). temp _ data collect: [: each | (each subStrings: #($,)) second asNumber]. data _ ((1 to: temp size) asArray sort: [: a : b | (temp at: a) < (temp at: b)]) collect: [: idx | data at: idx]

おまけ(高速版の意訳)

Smalltalk

data _ ((data collect: [: str | {(str subStrings: #($,)) second asNumber. str}]) 
   sort: [: a : b | a first < b first]) 
      collect: [: ary | ary second]


いやぁ…。Perl っていうのは表現力豊かですね。ぼくとつな Smalltalk に書き直してから読むとなんだか ほっとします。w

専用メソッドを用意

まつもとさんにコメントいただいたので、専用メソッドとして定義して用意してみましょう。

data sortBy: [: each | (each subStrings: #($,)) second asNumber]

Ruby ほど短くはならないですが、このほうがシンプルで分かりやすいですね。念のため #sortBy: の定義は(そのまんまですが)こんなかんじ。

ArrayedCollection >> sortBy: aBlock
   ^ ((self collect: [: each | {aBlock value: each. each}]) 
      sort: [: a : b | a first < b first]) 
         collect: [: ary | ary second]

"#(fig pear apple) sortBy: [: each | each size]"