use strict; use warnings; sub predchar # modify argument in place. # return true if no carry (done). { my $ord= ord($_[0]); my $nocarry; # define the ranges available my @ranges= ( [ord('0'),ord('9')], [ord('a'),ord('z')], [ord('A'),ord('Z')] ); foreach my $range (@ranges) { my ($first,$last)= @$range; next unless $ord >= $first && $ord <= $last; # my range? if ($ord == $first) { $ord= $last } else { --$ord; $nocarry= 1; } } $_[0]= chr($ord); return $nocarry; } sub magic_decrement($) { my @chars= split ('', shift); for (my $loop= $#chars; $loop>=0; --$loop) { last if predchar ($chars[$loop]); } return join ('',@chars); } sub test { my $s= shift || $_; my $result= magic_decrement($s); print qq{--"$s" gives "$result"\n}; } while () { chomp; test; } __DATA__ 123 abc testbaA ignore punct.