initial commit
[urisagit/Sort-Maker.git] / paper / field_sort.pl
CommitLineData
7468c584 1#!/usr/local/bin/perl
2
3use strict;
4
5# L. Rosler, January 10, 1999
6# Interface modeled after
7http://www.effectiveperl.com/recipes/sorting.html
8# by Joseph Hall.
9# @sorted = fieldsort( ['optional split pattern as string',]
10# [ anon array of specifiers (strings or optionally signed integers)
11# including, in any order:
12# optional '-' for reverse sort order,
13# required integer for field number (1 = first),
14# optional letter for field type: none = string;
15# 'sN' = first N chars of string; 'n' = optionally signed integer;
16# 'u' = unsigned integer; 'f' = optionally signed floating-point number
17],
18# @data);
19
20sub fieldsort {
21 my ($col, $cols, @len);
22 my $sep = !ref $_[0] && shift;
23 ref($cols = shift) eq 'ARRAY' or
24 die "Field specifiers must be in an anon array.\n";
25 my @cols = map { /^-(\d+)$|^(\d+)-$/ ? $+ : () } @$cols;
26 if (@cols) { # Preprocess variable-width negated string sorts.
27 @len = (0) x @$cols;
28 my $split = sub { length $sep ? split m{$sep} : split };
29 foreach (@_) {
30 my @a = $split->();
31 foreach $col (@cols) {
32 $len[$col - 1] = length $a[$col - 1]
33 if $len[$col - 1] < length $a[$col - 1];
34 }
35 }
36 }
37 my ($fixed, @packcode, @packargs) = 0;
38 for (@$cols) {
39 my ($col) = /(\d+)/ or die "No field specifier in $_.\n";
40 my ($let) = /([a-z])/;
41 unless ($let) { # variable-length string
42 push @packcode, 'A*x';
43 push @packargs,
44 "\$a[$col - 1]" . (/-/ && qq{ ^ "\\xFF" x $len[$col -
451]});
46 $fixed = -1;
47 } elsif ($let eq 's') { # fixed-length string or substr
48 my ($len) = /(\d+)$/ or die "No length specified for field
49$col.\n";
50 push @packcode, "A$len";
51 push @packargs, "\$a[$col - 1]" . (/-/ && qq{ ^ "\\xFF" x
52$len});
53 $fixed += $len if $fixed > -1;
54 } elsif ($let eq 'u') { # unsigned integer
55 push @packcode, 'N';
56 push @packargs, (/-/ && '-') . "\$a[$col - 1]";
57 $fixed += 4 if $fixed > -1;
58 } elsif ($let eq 'n') { # signed integer
59 push @packcode, 'N';
60 push @packargs, (/-/ && '-') . "\$a[$col - 1] ^ (1 << 31)";
61 $fixed += 4 if $fixed > -1;
62 } elsif ($let eq 'f') { # floating-point number
63 push @packcode, 'A8';
64 push @packargs, '_float_sort(' . (/-/ && '-') . "\$a[$col -
651])";
66 $fixed += 8 if $fixed > -1;
67 } else { die "Unrecognized type specifier $let in field $col.\n"
68}
69 }
70 eval 'map substr($_, ' . ($fixed < 0 ? '1 + rindex $_, "\0"' :
71$fixed) .
72 '), sort map { my @a = split' . (length $sep > 0 && " m{$sep}o")
73.
74 ";\npack '" . join("", @packcode) .
75 ($fixed < 0 && substr($packcode[-1], -1) ne 'x' && 'x') .
76 join(",\n", "A*'", @packargs, '$_') . '} @_'
77}
78
79BEGIN {
80 my $big_endian = pack('N', 1) eq pack('L', 1);
81 sub _float_sort {
82 (($big_endian ? pack 'd', $_[0] : reverse pack 'd', $_[0]) ^
83 ($_[0] < 0 ? "\xFF" x 8 : "\x80" . "\x00" x 7)) . $_[0]
84 }
85}
86
87
88# Example usage:
89
90my (@data, @sorted);
91@data = ('abc 2 zzz', 'abc 10 zzz', 'def 2 zzz');
92print q{@sorted = fieldsort [2, 1], @data}, "\n";
93@sorted = fieldsort [2, 1], @data;
94{ local $" = '|'; print "@sorted\n"; }
95@data = qw(abc+2+zzz abc+10+zzz abc+2+aaxx abc+2+ab def+2+zzz
96abc+-1+zzz);
97print q{@sorted = fieldsort '\\+', ['3s2', '-2n', -1], @data}, "\n";
98@sorted = fieldsort '\\+', ['3s2', '-2n', -1], @data;
99print "@sorted\n";
100@data = qw(abc:2:zzz abc:10:zzz abc:2:aaaa def:2:zzz abc:-1:zzz);
101print q{@sorted = fieldsort ':', ['2n', -1], @data}, "\n";
102@sorted = fieldsort ':', ['2n', -1], @data;
103print "@sorted\n";
104print q{@sorted = fieldsort ':', ['-2f', -1, '3-'], @data}, "\n";
105@sorted = fieldsort ':', ['-2f', -1, '3-'], @data;
106print "@sorted\n";
107print q{@sorted = fieldsort ':', ['-2n', '-1s3'], @data}, "\n";
108@sorted = fieldsort ':', ['-2n', '-1s3'], @data;
109print "@sorted\n";
110print q{@sorted = fieldsort ':', ['-2n', -1], @data}, "\n";
111@sorted = fieldsort ':', ['-2n', -1], @data;
112print "@sorted\n";
113
114