http://www.perlmonks.org?node_id=1063623

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

Hello Monks,

I am generating disk names and need to generate a list from a to X, where X is anything from z to zz.

At the moment, I'm generating a list then taking an array slice, e.g.:

#!/usr/bin/env perl use 5.010; use strict; use warnings; my @list = "a" .. "zz"; my @disk_list = qw(foo bar baz); my @new_list = @list[ 0 .. $#disk_list ]; map { say } @new_list;

The problem is, what if I need to go to "aaa", e.g. @disk_list is 703 long, this won't allow for that, and I think will generate an error, since @list[ 0 .. 703 ] would be out of bounds.

How can I generate a list from a to an arbitrary upper limit?

Thanks!

Acutually, the full sub is:

sub build_disk_list { my $self = shift; my $disk_names = shift; my @letters = 'a' .. 'zz'; my @disk_letters = @letters[ 0 .. $#{$disk_names} ]; my $disk_it = each_array @{$disk_names}, @disk_letters; my @disk_list; while ( my ( $disk_name, $disk_letter ) = $disk_it->() ){ push @disk_list, [$disk_name, "vd" . $disk_letter ]; } return \@disk_list; }

Edit: ok, a slice with an out of bound index would just return an empty value, which is even worse than generating an error

Solved Edit: Thanks very much everyone. I think I can make one of these solutions work.

Replies are listed 'Best First'.
Re: Generate list from a to arbitrary letter
by kennethk (Abbot) on Nov 20, 2013 at 23:02 UTC
    Auto increment and Auto decrement:
    The auto-increment operator has a little extra builtin magic to it. If you increment a variable that is numeric, or that has ever been used in a numeric context, you get a normal increment. If, however, the variable has been used in only string contexts since it was set, and has a value that is not the empty string and matches the pattern /^[a-zA-Z]*[0-9]*\z/, the increment is done as a string, preserving each character within its range, with carry:
    print ++($foo = "99");    # prints "100"
    print ++($foo = "a0");    # prints "a1"
    print ++($foo = "Az");    # prints "Ba"
    print ++($foo = "zz");    # prints "aaa"
    So, you could generate your list with:
    my $max = 703; my @letters; my $current = 'a'; for (1 .. $max) { push @letters, $current++; }
    or some close variant.

    #11929 First ask yourself `How would I do this without a computer?' Then have the computer do it the same way.

      ... a value that is not the empty string and matches the pattern  /^[a-zA-Z]*[0-9]*\z/ ...

      This part of the documentation is confusing to me because the empty string matches the pattern:

      >perl -wMstrict -le "$_ = ''; print 'match' if /^[a-zA-Z]*[0-9]*\z/; " match

      I'm sure I've seen a discussion of this point, but I cannot lay my hands on it at the moment. Anyone...?

        Well, there would be no point making two test if the result of one was the subset of the other (for exemple "a value that is positive and greater than 10"). In Perl, this condition would actually be: $matches if $_ ne '' and /^[a-zA-Z]*[0-9]*\z/ this was probably to have something easier to understand than one of those two patterns:

        /^(?: [a-zA-Z]+ [0-9]* | [a-zA-Z]* [0-9]+ ) \z/x; /^(?=.)[a-zA-Z]*[0-9]*\z/;
        The first one being a bit long, and the second not so beginner friendly.

      I think this is a very easy-to-use approach. The only thing I would add would be a comment line reminding whoever has to maintain it down the road to read the docs for how the auto-increment operator works with string values if they are confused by what they see.

      It helps to remember that the primary goal is to drain the swamp even when you are hip-deep in alligators.
Re: Generate list from a to arbitrary letter
by Eily (Monsignor) on Nov 20, 2013 at 23:20 UTC

    Building a list from a list looks like a job for map. But unless order is really important, and it isn't just alphabetical order (using sort is easy enough), a list of pairs looks like something that could have its place in a hash. So, based on kennethk's answer (Edit: and similar AnomalousMonk's proposition):

    $current = 'a'; my %cds = map { $_ => $current++ } @cd_names;
    It shouldn't be much trouble to turn that into a array of arrays if needed :).

Re: Generate list from a to arbitrary letter
by AnomalousMonk (Archbishop) on Nov 20, 2013 at 23:12 UTC

    A variant on kennethk's post:

    >perl -wMstrict -le "my @disk_list = qw(foo bar baz); ;; my $new = 'a'; my @new_list = map $new++, 0 .. $#disk_list; printf qq{'$_' } for @new_list; " 'a' 'b' 'c'
Re: Generate list from a to arbitrary letter
by LanX (Saint) on Nov 20, 2013 at 23:02 UTC
    something like this?

    DB<114> use POSIX DB<115> $times= ceil(log(703)/log(26)) => 3 DB<116> "z"x$times => "zzz"

    Cheers Rolf

    ( addicted to the Perl Programming Language)