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

I only want the letters A-Z, capital and/or lowercase, but I am getting some characters outside this range...
sub letter { my $offset = int rand 55; my $A = 65; chr($A + $offset); } sub city { my $chars = 10 + int rand 30; my $city; $city .= letter for (1..$chars); $city; }

Replies are listed 'Best First'.
Re: randomly generating A-Za-z
by pjf (Curate) on Oct 12, 2001 at 06:49 UTC
    G'day princepawn,

    dws noted the problem with your code above, with those pesky characters in between. You might find the following snippet useful.

    use constant (randchars => join("",'a'..'z','A'..'Z')); use constant (randchars_len => length(randchars)); sub letter { return substr(randchars,int(rand(randchars_len))); }
    Of course, TMTOWTDI. The above code uses constant strings to maximize speed.

    Cheers,
    Paul

Re: randomly generating A-Za-z
by Zaxo (Archbishop) on Oct 12, 2001 at 06:55 UTC

    This does the job:

    $ perl -e '@c=(A..Z,a..z);print $c[rand(scalar@c)|0]' F$

    Update: To keep your subs and make it strict:

    sub letter { ('A'..'Z','a'..'z')[rand(52)] } sub city { join '', map {letter()} 1..10 + int rand 30 }
    It would be fun to scan the zipcode db for character or digraph frequencies to make the dummy names more plausible.

    After Compline,
    Zaxo

Re: randomly generating A-Za-z
by dws (Chancellor) on Oct 12, 2001 at 06:32 UTC
    65 + int rand 55 doesn't do what you seem to expect. "Z" and "a" are not contiguous in ASCII. There are six pesky characters in between.

    Consult an ASCII chart for enlightentment.

Re: randomly generating A-Za-z
by Masem (Monsignor) on Oct 12, 2001 at 07:14 UTC
    A bit simpler:
    $city = join '', map { (a..z,A..z)[int rand 52] } (1..10+int rand 30);

    -----------------------------------------------------
    Dr. Michael K. Neylon - mneylon-pm@masemware.com || "You've left the lens cap of your mind on again, Pinky" - The Brain
    It's not what you know, but knowing how to find it if you don't know that's important

Re: randomly generating A-Za-z
by thinker (Parson) on Oct 12, 2001 at 18:02 UTC
    Hi princepawn,

    Here's my solution. I'm sure it's not as efficient as some of the others, but its mine, and I like it.:-)

    sub letter{ return rand(2)%2 ? chr rand(26)+65 : chr rand(26)+97; }
    Fortunately chr() rounds down the random value, saving a couple of int() calls.
    I hope this is correct.
    cheers

    thinker
      Hi thinker
      I'm sure it's not as efficient as some of the others,
      but its mine, and I like it.:-)

      Looking back up the thread, I see plenty of hyperefficient
      solutions to this. Well, here's my shot at the least efficient
      solution... apologies to princepawn who probably isn't aided
      in the least by this post

      sub fisher_yates() { for(my $i = @_; --$i) { my $j = int rand ($i+1); next if $j == $i; @_[$i,$j] = @_[$j,$i]; } return @_; } sub letter() { for(@_) { my $k = int(rand 52) - 1; return chr $_[$k] if($_[$k] eq $_); } &letter; } # &letter isn't completely random... # print(&letter(65..90,97..122),"\n"); # ...so use Perl Cookbook's Fisher- # Yate Shuffle to randomize the list print(&letter(&fisher_yates(65..90,97..122),"\n");

      blyman

      Update: Running above code several times has
      confirmed my initial suspicion: &letter() as written is
      more likely to return an 'A' than a 'z'. Fixed this by adding
      a Fisher-Yates shuffle; this helps fulfill my original goal
      of writing the least-efficient algorithm for returning a
      random letter...

Re: randomly generating A-Za-z
by George_Sherston (Vicar) on Oct 12, 2001 at 16:47 UTC
    In the spirit of timtowtdi, and if you're not pressed for time, you could do it with magical incrementation:
    sub letter { my $offset = int rand 26; my $A = 'a'; $A++ for 0..$offset; return $A; }


    § George Sherston