Range function

David Formosa (aka ? the Platypus) dformosa at dformosa.zeta.org.au
Sun May 15 22:22:16 EDT 2005


On 15 May 2005 02:50:38 -0700, Xah Lee <xah at xahlee.org> wrote:
> Here's the Perl code.

Where did you learn to program?  Its highly unlikely that a Perl
programer would ever write a range function as there is a built in
Perl function that does the same thing.  If your intent is purely
accedemic then the first thing you should do is learn Perl to a much
higher grade.

> #! perl
> 
> # http://xahlee.org/tree/tree.html
> # Xah Lee, 2005-05
> 
> #_____ Range _____ _____ _____ _____
> 
>=pod
> 
> B<Range>

Its considered poor style to have function names with capital
letters.  The normal convention is to use all lower case.

> Range($iMax) generates the list [1, 2, ... , $iMax].
> 
> Range($iMin, $iMax) generates the list [$iMin, ... , $iMax].
> 
> Range($iMin, $iMax, $iStep) uses increment $iStep, with the last
> element in the result being less or equal to $iMax. $iStep cannot be 0.
> If $iStep is negative, then the role of $iMin and $iMax are reversed.
> 
> If Range fails, 0 is returned.
> 
> Example:
> 
>  Range(5); # returns [1,2,3,4,5]
> 
>  Range(5,10); # returns [5,6,7,8,9,10]
> 
>  Range( 5, 7, 0.3); # returns [5, 5.3, 5.6, 5.9, 6.2, 6.5, 6.8]
> 
>  Range( 5, -4, -2); # returns [5,3,1,-1,-3]
> 
>=cut

sub range {

  return [1..$_[0]]     if (@_==1);

  return [$_[0]..$_[1]] if (@_==2);

  my $lowest    = shift;
  my $greatest  = shift;
  my $increment = shift;

  my $steps     = ($greatest - $lowest)/$increment;
  my @return    = map { $_ * $increment + $lowest } (0..$steps);

  return \@return;
}

This does as you wish but it far shorter and I would argue easyer for
the typical perl programer to read.

> sub Range ($;$$) {
> if (scalar @_ == 1) {return _rangeFullArgsWithErrorCheck(1,$_[0],1);};
> if (scalar @_ == 2) {return
> _rangeFullArgsWithErrorCheck($_[0],$_[1],1);};
> if (scalar @_ == 3) {return
> _rangeFullArgsWithErrorCheck($_[0],$_[1],$_[2]);};
>};

I would suggest that If you have the case where your doing a one line
if stament then you should make use of the line modifing verent of
if.  Also since if produces a scalar context its not needed.

sub Range ($;$$) {
  return _rangeFullArgsWithErrorCheck(1,$_[0],1)         if (@_ == 1);
  return _rangeFullArgsWithErrorCheck($_[0],$_[1],1)     if (@_ == 2);
  return _rangeFullArgsWithErrorCheck($_[0],$_[1],$_[2]) if (@_ == 3);
}

See how much neater and more readable the code is after doing that.

> sub _rangeFullArgsWithErrorCheck ($$$) {
> my ($a1, $b1, $dx) = @_;
> 
> if ($dx == 0) {print "Range: increment cannot be zero."; return 0}
> elsif ($a1 == $b1) {return [$a1];}
> elsif ( ((($b1 - $a1) > 0) && ($dx < 0)) || ((($b1 - $a1) < 0) && ($dx
>> 0)) ) {print "Range: bad arguments. You have [$a1,$b1,$dx]"; return
> 0;}
> elsif ((($a1 < $b1) && ($b1 < ($a1 + $dx))) || (($a1 > $b1) && ($b1 >
> ($a1 + $dx)))) {return [$a1];}
> else { return _rangeWithGoodArgs ($a1,$b1,$dx);};
>};

This would be a great place to make use of die.  Throwing an exection for an 
error.

sub _rangeFullArgsWithErrorCheck ($$$) {
  my ($a1, $b1, $dx) = @_;

  die "Range: increment cannot be zero." unless $dx;

  return [$a1] if ($a1 == $b1);

  if ( ((($b1 - $a1) > 0) && ($dx < 0))
        || 
       ((($b1 - $a1) < 0) && ($dx0))) {
    die "Range: bad arguments. You have [$a1,$b1,$dx]";
  }
}


> sub _rangeWithGoodArgs ($$$) {
> my ($a1, $b1, $dx) = @_;
> my @result;
> 
> if ($a1 < $b1) {for (my $i = $a1; $i <= $b1; $i += $dx) { push
> (@result, $i);}; }
> else {for (my $i = $a1; $i >= $b1; $i += $dx) { push (@result, $i);};
>};
> return \@result;
>};

Personally I don't like the c style while loop.  I didn't like it in C
and I don't like it in perl.

-- 
Please excuse my spelling as I suffer from agraphia. See
http://dformosa.zeta.org.au/~dformosa/Spelling.html to find out more.
Free the Memes.



More information about the Python-list mailing list