ibm1620 has asked for the wisdom of the Perl Monks concerning the following question:

I'm composing string values from the ordered set of consonants ('BCDFG .. Z') and I want to be able to increment them; e.g.
$val = 'BB'; incr($val); ==> 'BC' $val = 'BZ'; incr($val); ==> 'CB'; $val = 'ZZ'; incr($val) ==> 'BBB';
So, similar to the neat feature of applying the ++ operator to a variable containing a string, but using only consonants as the "digits".

I'm sure there must be a simple, slick Perl algorithm for incrementing using an arbitrary set of digits like this.

(UPDATE) Here's what I have written - I think it works, and it illustrates one way of doing it. Note that $digits can be any string of distinct characters and it will still "count".

use 5.010; use strict; use warnings; my $x = 'ZX'; for my $i ( 1 .. 100 ) { $x = incra($x); say $x; } sub incra { my $val = shift or die "Where's my value?"; $val = reverse $val; # easier to think left to right my $digits = 'BCDFGHJKLMNPQRSTVWXZ'; my $ndigits = length $digits; my $carry_in = 1; my $result = ''; for my $digit ( split //, $val ) { my $i = index $digits, $digit; if ($carry_in) { if ( $i == $ndigits - 1 ) { $digit = substr $digits, 0, 1; } else { $digit = substr $digits, $i + 1, 1; $carry_in = 0; } } $result .= $digit; } $result .= substr $digits, 0, 1 if $carry_in; return reverse $result; }

Replies are listed 'Best First'.
Re: Algorithm for "Incrementing" strings
by Not_a_Number (Prior) on Feb 11, 2015 at 19:22 UTC
    sub incr { my $str = shift; ++$str; $str =~ y/AEIOU/BFJPV/; return $str; }
      Now THAT'S pretty slick...
Re: Algorithm for "Incrementing" strings
by pme (Monsignor) on Feb 11, 2015 at 18:27 UTC
    ++ operator can be applied to strings and then skip vowels:
    use strict; use warnings; sub consinc { my $str = shift; my %vowels = ( 'a' => 1, 'e' => 1, 'i' => 1, 'o' => 1, 'u' => 1, ); do { $str++; } while (exists $vowels{substr($str, -1 , 1)}); return $str; } my $str = $ARGV[0]; print "$str - " . consinc($str) . "\n";
    update

    'if' can do:

    $str++ if exists $vowels{substr($str, -1 , 1)};
      True, although the more digits $val has, the longer your do loop will have to spin to get past 'XAAAAAAAAAAAA' and up to 'XBBBBBBBBBBBB', won't it?
        If we always skip vowels then we never have vowels in the string. Should we replace vowels in the string in the very beginning?
Re: Algorithm for "Incrementing" strings (fleximal)
by tye (Sage) on Feb 12, 2015 at 01:52 UTC
Re: Algorithm for "Incrementing" strings
by Anonymous Monk on Feb 13, 2015 at 21:52 UTC

    It's so simple it doesn't even need a sub:

    #!/usr/bin/perl use strict; use warnings; my $consonants = 'BCDFGHJKLMNPQRSTVWXZ'; my @letters = split //, $consonants; my ($first, $last) = @letters[0, -1]; my %next; @next{'', @letters} = @letters; my $x = 'B'; for (1..450) { print "$x "; $x =~ s/([^$last]?)($last*)$/ $next{$1} . $first x length $2/ e; } print "\n";

      And with only a few tweaks, we can decrement strings:

      #!/usr/bin/perl use strict; use warnings; my $consonants = 'BCDFGHJKLMNPQRSTVWXZ'; my @letters = split //, $consonants; my ($first, $last) = @letters[0, -1]; my %prev; @prev{@letters} = ('', @letters); my $x = 'BCD'; while( length $x ) { print "$x "; $x =~ s/(.)($first*)$/ $prev{$1} . $last x length $2 /e; } print "\n";

      Oops, typo

      $x =~ s/([^$last]?)($last*)$/ $next{$1} . $first x length $2 /e;

        The general "increment a string" algorithm popped into my head much later so I checked back and was happy to see that it had already been posted. Here is my formulation of it that requires no set-up (easy for a human to compose when told what "digits" to use but not appropriate to use if the digits are not pre-set):

        s{([^Z])?(Z*)$}{ local $_ = ( $1 // 'Z' ) . $2; tr/BCDFGHJ-NP-TV-Z/CDFGHJ-NP-TV-ZB/; $_ }e;

        Decrement:

        s{(([^B])|B)(B*)$}{ local $_ = ( $2 // '' ) . $3; tr/CDFGHJ-NP-TV-ZB/BCDFGHJ-NP-TV-Z/; $_ }e;

        - tye        

A reply falls below the community's threshold of quality. You may see it by logging in.