[perl-python] Range function

X

Xah Lee

Today we'll be writing a function called Range. The Perl documentation
is as follows.

Perl & Python & Java Solutions will be posted in 48 hours.

This is Perl-Python a-day. See
http://xahlee.org/web/perl-python/python.html

Xah
(e-mail address removed)
∑ http://xahlee.org/

--------------------------

Range

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]
 
J

Jürgen Exner

Xah said:
Today we'll be writing a function called Range.

I don't think so. Unless you meant to write "Today WE'll be writing ...."
The Perl documentation is as follows.

Bullshit. The Perl documentation is part of any Perl installation but you
didn't link to it anywhere left alone quote it.
Actually I'm glad you didn't, it's quite large after all.

jue
 
X

Xah Lee

Here's the Perl code.

----------

#! perl

# http://xahlee.org/tree/tree.html
# Xah Lee, 2005-05

#_____ Range _____ _____ _____ _____

=pod

B<Range>

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 ($;$$) {
if (scalar @_ == 1) {return _rangeFullArgsWithErrorCheck(1,$_[0],1);};
if (scalar @_ == 2) {return
_rangeFullArgsWithErrorCheck($_[0],$_[1],1);};
if (scalar @_ == 3) {return
_rangeFullArgsWithErrorCheck($_[0],$_[1],$_[2]);};
};

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);};
};

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;
};

#end Range

##########
# test

use Data::Dumper;
print Dumper(Range(5,7,0.3));
 
X

Xah Lee

Here's the Python solution.

----------
# -*- coding: utf-8 -*-
# Python

# http://xahlee.org/tree/tree.html
# Xah Lee, 2005-05

# implementation note: When iStep is a decimal, rounding error
# accumulates. For example, the last item returned from
# Range(0,18,0.3) is 17.7 not 18. A remedy is to turn iStep into a
# fraction and do exact arithmetics, and possibly convert the result
# back to decimal. A lesser workaround is to split the interval as to
# do multiple smaller range and join them together.

def Range(iMin, iMax=None, iStep=None):
if (iMax==None and iStep==None):
return Range(1,iMin)
if iStep==None:
return Range(iMin,iMax,1)
if iMin <= iMax and iStep > 0:
if (isinstance(iStep,int) or isinstance(iStep,long)):
return range( iMix, iMax, iStep)
else:
result=[];temp=iStep
while iMin <= iMax:
result.append(iMin)
iMin += iStep
return result

# test
print Range(0, 18, 0.3)
 
P

Peter Hansen

Xah said:
Here's the Python solution.
# implementation note: When iStep is a decimal, rounding error
# accumulates. For example, the last item returned from
# Range(0,18,0.3) is 17.7 not 18. A remedy is to turn iStep into a
# fraction and do exact arithmetics, and possibly convert the result
# back to decimal. A lesser workaround is to split the interval as to
# do multiple smaller range and join them together.

Good lord no! The correct way is to use an integer count and simply
multiply it each time by the step, and add that to the range. No
accumulation of errors then. Where did you learn to program?
(Rhetorical question of course, as you haven't, yet.)
def Range(iMin, iMax=None, iStep=None):
if (iMax==None and iStep==None):
return Range(1,iMin)
if iStep==None:
return Range(iMin,iMax,1)
if iMin <= iMax and iStep > 0:
if (isinstance(iStep,int) or isinstance(iStep,long)):
return range( iMix, iMax, iStep)
else:
result=[];temp=iStep
while iMin <= iMax:
result.append(iMin)
iMin += iStep
return result

That's some of the worst Python code I've seen recently. Please, no one
take this as representative of how decent Python programmers write code.

-Peter
 
X

Xah Lee

the previous posted solutions are badly botched.

Here's a better solution. Any further correction will appear on the
website instead. (http://xahlee.org/tree/tree.html)

Similar change needs to be made for the Perl code... Java code will
come tomorror.

By the way, the code from me are not expected to be exemplary. These
are exercises for all, also as a intro to functional programing to
industry programers. Also, later on there will be non-trivial problems.

# -*- coding: utf-8 -*-
# Python

# http://xahlee.org/tree/tree.html
# Xah Lee, 2005-05

import math;

def Range(iMin, iMax=None, iStep=None):
if (iMax==None and iStep==None):
return Range(1,iMin)
if iStep==None:
return Range(iMin,iMax,1)
if iMin <= iMax and iStep > 0:
if (isinstance(iStep,int) or isinstance(iStep,long)):
return range( iMin, iMax+1, iStep)
else:
result=[]
for i in range(int(math.floor((iMax-iMin)/iStep))+1):
result.append( iMin+i*iStep)
return result
if iMin >= iMax and iStep < 0:
if (isinstance(iStep,int) or isinstance(iStep,long)):
return range( iMin, iMax-1, iStep)
else:
result=[]
for i in range(int(math.floor((iMin-iMax)/-iStep))+1):
result.append( iMin+i*iStep)
return result
# raise error about bad argument. To be added later.

# test
print Range(5,-4,-2)

# Thanks to Peter Hansen for a correction.

Xah
(e-mail address removed)
∑ http://xahlee.org/
 
P

Peter Hansen

Xah said:
the previous posted solutions are badly botched.
def Range(iMin, iMax=None, iStep=None):
[snip hideous code]
# Thanks to Peter Hansen for a correction.

Ohmigod, he's only made it worse and he's blaming me for it. Shows what
I get for replying to a cross-posted troll message.

Xah, don't use my name in reference to anything you do, ever, even if
it's only to try to give me credit.

-Peter
 
D

David Formosa (aka ? the Platypus)

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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,995
Messages
2,570,231
Members
46,820
Latest member
GilbertoA5

Latest Threads

Top