sub isnum($) { $_[0] =~ m{^\s* [-+\d.]* [\d.]+ (?:e[-+]\d+)? \s* $}x } sub Cmp (;$$$); sub Cmp (;$$$) { my $r=0; require P; my ($a, $b, $d) = @_ ? @_ : ($a, $b); my ($ra, $rb) = (ref $a, ref $b); my ($ta, $tb) = (typ $a, typ $b); P::Pe("ta=%s, tb=%s", $ta, $tb) if $d; P::Pe("ra=%s, rb=%s", $ra, $rb) if $d; my ($dta, $dtb) = (defined $ta, defined $tb); # first handle "values" (neither are a type reference) unless($dta || $dtb) { $r = isnum($a) && isnum($b) ? $a <=> $b : $a cmp $b; P::Pe("isnum, a=%s, b=%s, r=%s", isnum($a), isnum($b), $r) if $d; return $r } # then handle unequal type references elsif ($dta ^ $dtb) { return (undef, 1) } elsif ($dta && $dtb && $ta ne $tb) { return (undef, 2) } # now, either do same thing again, or handle differing classes # the no-class on either implies no type-ref on either & is handled above my ($dra, $drb) = (defined $ra, defined $rb); if ($dra ^ $drb) { return (undef, 3) } elsif ($dra && $drb && $ra ne $rb) { return (undef, 4) } # now start comparing references: dereference and call Cmp again if ($ta eq SCALAR) { return Cmp($$a, $$b) } elsif ($ta eq ARRAY) { P::Pe("len of array a vs. b: (%s <=> %s)", @$a, @$b) if $d; return $r if $r = @$a <=> @$b; # for each member, compare them using Cmp for (my $i=0; $i<@$a; ++$i) { P::Pe("a->[i] Cmp b->[i]...\0x83", $a->[$i], $b->[$i]) if $d; $r = Cmp($a->[$i], $b->[$i]); P::Pe("a->[i] Cmp b->[i], r=%s", $a->[$i], $b->[$i], $r) if $d; return $r if $r; } return 0; # arrays are equal } elsif ($ta eq HASH) { my @ka = sort keys %$a; my @kb = sort keys %$b; $r = Cmp(0+@ka, 0+@kb); P::Pe("Cmp #keys a(%s) b(%s), in hashes: r=%s", 0+@ka, 0+@kb, $r) if $d; return $r if $r; $r = Cmp(\@ka, \@kb); P::Pe("Cmp keys of hash: r=%s", $r) if $d; return $r if $r; my @va = map {$a->{$_}} @ka; my @vb = map {$b->{$_}} @kb; $r = Cmp(\@va, \@vb); P::Pe("Cmp values for each key, r=%s", $r) if $d; return $r; } else { P::Pe("no comparison for type %s, ref %s", $ta, $ra) if $d; return (undef,5); ## unimplemented comparison } }