89ou 发表于 2015-12-25 15:37:57

Perl排序:施瓦茨变换

  给定一个数组a = (a1, a2, ... , an)和一个函数fn(),现在对数组(fn(a1), fn(a2), ... , fn(an))排序。
  如果用下面的方法直接排序的话,会存在很多的对fn()重复计算的情况。如果函数fn()比较复杂,这种重复会显得更加突出。
  施瓦茨变换就是为了解决这个对fn()重复计算问题,如下:




1 my @sorted =
2   map $_->,
3   sort { $b-> <=> $a-> }
4   map [ $_, f($_) ],
5   @original;   
  
泛化一点:




1 my @sorted =
2   map $_->,
3   sort { SORT COMPARISON USING $a-> AND $b-> }
4   map [ $_, EXPENSIVE FUNCTION OF $_ ],
5   @original;
6
  
举例:




1 # case insensitive sorting in alphabetical order
2my @sorted =
3   map $_->,
4   sort { $a-> cmp $b-> }
5   map [ $_, "\U$_" ],
6   @original
  
在三级排序中使用施瓦茨变换:




1 # 首先用SOME FUNCTION排序,再用ANOTHER排序,最后用
2 # YET ANOTHER排序
3my @sorted =
4   map $_->,
5   sort { SORT COMPARISON USING $a-> AND $b-> or
6         ANOTHER USING $a-> AND $b-> or
7       YET ANOTHER USING $a-> AND $b-> }
8   map [ $_, SOME FUNCTION OF $_, ANOTHER, YET ANOTHER ],
9   @original;
10
  或者将每次计算的结果保存一下,这也避免了重复计算(举例用cmp做字符串比较):




1 @sorted = do {
2   my %fv;
3   sort { ($fv{$a} ||= f($a))
4                   cmp
5                ($fv{$b} ||= f($b))
6             }
7             @original
8 };
  
或者把用于cache的hash定义在do block外:




1 my %fv;
2@sorted =
3   sort { (fv{$a} ||= f($a))
4                  cmp
5                (fv{$b} ||= f($b))
6   }
7   @original;
  或者事先把所有的fn()计算出来:




1 my %fv;
2@gv{@original} = map { fn($_) } @original;
3@sorted = sort { $fv{$a} cmp $v{$b} } @original;
  
或者用Memoize模块:




use Memoize;
memoize('fn');
@sorted = sort { fn($a) cmp fn($b) } @original;
  
页: [1]
查看完整版本: Perl排序:施瓦茨变换