Newton's Method in perl - perl

Okay so for my math class we were asked to write a program that performs and prints Newton's method until the values converge and we have a root for the function. At first I thought it would be easy. It was until I just couldn't get the values derived from the first time to be used the second time. My knowledge of the language is basic. Really basic, so what you're about to see might not be pretty.
#!usr/bin/perl
use PDL;
print "First guess? (this is x0)\n";
$xorig = <>;
do {
&fx;
} until ($fex == 0);
sub fx {
if ($xn == 0) {
$x = $xorig;
}
else {
$x = $xn;
}
print "What is the coefficient (for each factor) of your function?\n";
$fcx = <STDIN>;
push #coefficient_of_x, $fcx;
print "... times x to the (enter exponent, if no exponent, enter 1. if no x, enter 0)?\n";
$fex = <STDIN>;
push #exponent_x, $fex;
chomp ($fcx, $fex, $x, $xorig);
$factor = $fcx * ($x ** $fex);
push #fx, $factor;
}
my $fx = 0;
foreach my $variable (#fx) {
$fx = $variable + $fx #THIS PROVIDES A VALUE FOR THE GIVEN F(X) WITH A GIVEN X VALUE
}
print "f($x)=$fx\n";
do {
&fprimex;
} until ($fprimeex == 0);
sub fprimex {
if ($xn == 0) {
$x = $xorig;
}
else {
$x = $xn;
}
print "What is the coefficient (for each factor) of your derivative function?\n";
$fprimecx = <STDIN>;
push #coefficient_of_fpx, $fprimecx;
print "... times x to the (enter exponent, if no exponent, enter 1. if no x, enter 0)?\n";
$fprimeex = <STDIN>;
push #exponent_fpx, $fprimeex;
chomp ($fprimecx, $fprimeex, $x, $xorig);
$factorprime = $fprimecx * ($x ** $fprimeex);
push #fprimex, $factorprime;
}
$fprimex = 0;
foreach my $variableprime (#fprimex) {
$fprimex = $variableprime + $fprimex #THIS PROVIDES A VALUE FOR THE GIVEN F'(X) WITH THAT SAME X VALUE
}
print "f'($x)=$fprimex\n";
sub x0 {
$xn = $xorig - $fx / $fprimex; #THIS IS NEWTON'S METHOD EQUATION FOR THE FIRST TIME
push #newxn, $xn;
print "xn ia $xn\n";
}
&x0;
foreach $value (#exponent_x) {
$exponent_x = $xn ** $value;
push #part1, $exponent_x;
$part1 = #part1;
}
foreach $value2 (#coefficient_of_x) {
$part2 = $value2 * #part1;
push #final1, $part2;
}
print "#part1\n";
print "#final1\n";
Essentially what it is is I first ask for the first guess. I use this value to define the coefficients and the exponents of f(x) to get a value for f(x) in terms of the given x. I do it again for f'(x). Then I perform newton's method the first time and get the new value xn. But I'm having a hard time to get values for f(xn) and f'(xn), meaning I can't get x(n+1) and can't continue newton's method. I need help.

Welcome to Perl.
I would strongly recommend the following changes to your code:
Always include use strict; and use warnings; in EVERY Perl script.
Always chomp your input from STDIN as your taking it:
chomp( my $input = <STDIN> );
Don't needlessly create subroutines, especially for one-off scripts such as this.
Instead of using the statement modifier form of do, I would recommend using an infinite while with loop control statements to exit:
while (1) {
last if COND;
}
Finally, since the coefficients of your polynomial are all associated with an exponent for X, I would recommend using a %hash for conveniently saving those values.
As demonstrated:
#!usr/bin/env perl
use strict;
use warnings;
print "Build your Polynomial:\n";
my %coefficients;
# Request each Coefficient and Exponent of the Polynomial
while (1) {
print "What is the coefficient (for each factor) of your function? (use a bare return when done)\n";
chomp( my $coef = <STDIN> );
last if $coef eq '';
print "... times x to the (enter exponent, if no exponent, enter 1. if no x, enter 0)?\n";
chomp( my $exp = <STDIN> );
$coefficients{$exp} = $coef;
}
print "\nFirst guess? (this is x0)\n";
chomp( my $x = <> );
# Newton's Method Iteration
while (1) {
my $fx = 0;
my $fpx = 0;
while ( my ( $exp, $coef ) = each %coefficients ) {
$fx += $coef * $x**$exp;
$fpx += $coef * $exp * $x**( $exp - 1 ) if $exp != 0;
}
print " f(x) = $fx\n";
print " f'(x) = $fpx\n";
die "Slope of 0 found at $x\n" if $fpx == 0;
my $new_x = $x - $fx / $fpx;
print "Newton's Method gives new value for x at $new_x\n";
if ( abs($x - $new_x) < .0001 ) {
print "Accuracy reached\n";
last;
}
$x = $new_x;
}

I am having trouble working out what you intended with your code. The main problem seems to be that don't have it clear in your head what each of your subroutines do, as fx and fprimex ask for the data as well as evaluating the function (except for summing the terms which, oddly, is done outside the subroutine). That isn't what you want at all, as the exponents and coefficients remain constant throughout a program that has to evaluate those functions many times, and you really don't want to ask for the values again each time.
Here are some pointers to getting Perl code working in general
Write your program in tiny chunks -- a line or two at a time -- and check after each addition that the program compiles and runs and produces the expected results. Writing an entire program before you even try to run it is a recipe for disaster
Always use strict and use warnings, and declare every variable with my as close as possible to the point where it is first used. You have many undeclared variables which are therefore global, and passing information between sections of code using global variables is a good way to lose yourself in your own code. It is a good rule for a subroutine to use only parameters passed to it or variables declared within it
chomp variables as soon as they are read, either from a file or from the terminal. A useful idiom to trim input strings at source is
chomp(my $input = <>)
which will ensure that there are no stray newlines anywhere in your data
That at least should get you started.
I'm in two minds about showing this. I hope it will help you, but I really don't want to drag you into parts of Perl that you're not familiar with.
It's a program that uses the Newton–Raphson method to find the root of polynomials. I've skipped the terminal input for now, and hard-coded the data. As it stands it finds the square root of 3,000 by finding the positive root of x2 - 3000.
Note that #f and #f_prime hold the coefficients of the function and its derivative backwards from the usual order, so #f is (-3000, 0, 1). The program also calculates the coefficients of the derivative function, as it is a simple thing to do and far preferable to asking the user for another set of values.
There is just one subroutine polynomial, which takes a value for x and a list of coefficients. This is used to calculate the value of both the function and its derivative for a given value of x.
The algorithm step is in the line
my $new_x = $x - polynomial($x, #f) / polynomial($x, #f_prime);
which calculates x - f(x) / f'(x) and assigns it to $new_x. Each successive estimate is printed to STDOUT until the loop exits.
Comparing floating-point values for equality is a bad idea. The precision of computer floating-point values is, obviously, limited, and a sequence will probably never converge to the point where the last two values of the sequence are equal. The best that can be done is to check whether the absolute value of the difference between the last two values is below a reasonable delta. An accuracy of 10E12 is reasonable for 32-bit floating-point numbers. I have found that the series converges to within 10E14 quite reliably, but if you find that your program hangs in an endless loop then you should increase the margin.
use strict;
use warnings;
my #f = reverse (1, 0, -3000);
my #f_prime = map { $f[$_] * $_ } 1 .. $#f;
my $x = 0.5;
print $x, "\n";
while () {
my $new_x = $x - polynomial($x, #f) / polynomial($x, #f_prime);
last if abs($new_x - $x) < $x / 1e14;
$x = $new_x;
print $x, "\n";
}
sub polynomial {
my ($x, #coeffs) = #_;
my $total = 0;
my $x_pow = 1;
for my $coeff (#coeffs) {
$total += $x_pow * $coeff;
$x_pow *= $x;
}
$total;
}
output
0.5
3000.25
1500.62495833681
751.312062703027
377.652538627869
192.798174296885
104.179243809523
66.4878834504349
55.8044433107163
54.7818016853582
54.7722565822241
54.7722557505166

Related

perl - short factorial calculator returning long strings of 1's

I'm trying to make a program that calculates the factorial of a number. I'm not very familiar with perl, so I think I'm missing some grammar rule.
When I enter 5 the program should return 120. Instead it returns a few dozen 1's. When I try other numbers I still get 1's, but more or less of them depending on if I enter a higher or lower number.
Here's my code:
print"enter a positive # more than 0: \n";
$num = <STDIN>;
$fact = 1;
while($num>1)
(
$fact = $fact x $num;
$num = $num - 1;
)
print $fact;
system("pause");
This is my first post to stack Overflow, so I hope I obeyed all theposting rules.
The problem is this line:
$fact = $fact x $num;
x is not the multiplication operator in Perl. It is used to repeat things. 1 x 5 will produce "11111".
Instead you want *.
$fact = $fact * $num;
Which can be written using *=.
$fact *= $num;
A few other issues...
Get used to strict and warnings now. By default, Perl will let you use variables without declaring them. They are global, which is bad for reasons you'll learn later. Right now it means if you have a typo in a variable name like $face Perl won't tell you about it.
Looping over a list of numbers is better done with a for loop over a range using ...
# Loop from 1 to $num setting $factor each time.
for my $factor (1..$num) {
$fact *= $factor;
}
Instead of using a system call to pause the program, use sleep.

Binary search—Can't use string "1" as a symbol ref while strict refs is in use

I've been browsing over the already answered questions regarding this error message.
I am trying to solve a problem from the Rosalind web site that looks for some indexes using a binary search.
When my subroutine finds the number it seems to ignore it, and if I try to print the $found variable, it gives me the error
Can't use string "1" as a symbol ref while strict refs is in use
The code is this
sub binarysearch
{
my $numbertolook = shift;
my #intarray=#_;
my $lengthint = scalar #intarray;
my #sorted = sort {$a <=> $b} #intarray;
#print $numbertolook, " " , #sorted, "\n";
my $low=0;
my $high=$lengthint-1;
my $found =undef;
my $midpoint;
while ($low<$high)
{
$midpoint=int(($low+$high)/2);
#print $midpoint, " ",$low," ", $high, " ", #sorted, "\n";
if ($numbertolook<$sorted[$midpoint])
{
$high=$midpoint;
}
elsif ($numbertolook>$sorted[$midpoint])
{
$low=$midpoint;
}
elsif ($numbertolook==$sorted[$midpoint])
{
$found=1;
print $found "\n";
last;
}
if ($low==$high-1 and $low==$midpoint)
{
if ($numbertolook==$sorted[$high])
{
$found=1;
print $found "\n";
last;
}
$low=$high;
}
}
return $found;
}
You want
print $found, "\n";
Or
print $found . "\n";
With no operator between $found and the newline, it thinks $found is the filehandle to print a newline to, and is getting an error because it isn't a filehandle.
I'll try to help
First of all, as simple as it may seem, a binary search is quite difficult to code correctly. The main reason is that it's a hotbed of off-by-one errors, which are so prevalent that they have their own Wikipedia page
The issue is that an array containing, say, the values A to Z will have 26 elements with indices 0 to 25. I think FORTRAN bucks the trend, and Lua, but pretty much every other language has the first element of an array at index zero
A zero base works pretty well for everything until you start using divide and conquer algorithms. Merge Sort as well as Binary Search are such algorithms. Binary search goes
Is it in the first half?
If so then search the first half further
Else search the second half further
The hard part is when you have to decide when you've found the object, or when you need to give up looking. Splitting data in two nearly-halves is easy. Knowing when to stop is hard
It's highly efficient for sorted data, but the problem comes when implementing it that, if we do it properly, we have to deal with all sorts of weird index bases beyond zero or one.
Suppose I have an array
my #alpha = 'A' .. 'Q'
If I print scalar #alpha I will see 17, meaning the array has seventeen elements, indexed from 0 to 16
Now I'm looking for E in that array, so I do a binary search, so I want the "first half" and the "second half" of #alpha. If I add 0 to 16 and divide by 2 I get a neat "8", so the middle element is at index 8, which is H
But wait. There are 17 elements, which is an odd number, so if we say the first eight (A .. H) are left of the middle and the last eight (I .. Q) are right of the middle then surely the "middle" is I?
In truth this is all a deception, because a binary search will work however we partition the data. In this case binary means two parts, and although the search would be more efficient if those parts could be equal in size it's not necessary for the algorithm to work. So it can be the first third and the last two-thirds, or just the first element and the rest
That's why using int(($low+high)/2) is fine. It rounds down to the nearest integer so that with our 17-element array $mid is a usable 8 instead of 8.5
But your code still has to account for some unexpected things. In the case of our 17-element array we have calculated the middle index to be 8. So indexes 0 .. 7 are the "first half" while 8 .. 16 are the "second half", and the middle index is where the second half starts
But didn't we round the division down? So in the case of an odd number of elements, shouldn't our mid point be at the end of the first half, and not the start of the second? This is an arcane off-by-one error, but let's see if it still works with a simple even number of elements
#alpha = `A` .. `D`
The start and and indices are 0 and 3; the middle index is int((0+3)/2) == 1. So the first half is 0..1 and the second half is 2 .. 3. That works fine
But there's still a lot more. Say I have to search an array with two elements X and Y. That has two clear halves, and I'm looking for A, which is before the middle. So I now search the one-element list X for A. The minimum and maximum elements of the target array are both zero. The mid-point is int((0+0)/2) == 0. So what happens next?
It is similar but rather worse when we're searching for Z in the same list. The code has to be exactly right, otherwise we will be either searching off the end of the array or checking the last element again and again
Saving the worst for last, suppose
my #alpha = ( 'A', 'B, 'Y, 'Z' )
and I'm looking for M. That lest loose all sorts of optimisations that involve checks that may may the ordinary case much slower
Because of all of this it's by far the best solution to use a library or a language's built-in function to do all of this. In particular, Perl's hashes are usually all you need to check for specific strings and any associated data. The algorithm used is vastly better than a binary search for any non-trivial data sets
Wikipedia shows this algorithm for an iterative binary search
The binary search algorithm can also be expressed iteratively with two index limits that progressively narrow the search range.
int binary_search(int A[], int key, int imin, int imax)
{
// continue searching while [imin,imax] is not empty
while (imin <= imax)
{
// calculate the midpoint for roughly equal partition
int imid = midpoint(imin, imax);
if (A[imid] == key)
// key found at index imid
return imid;
// determine which subarray to search
else if (A[imid] < key)
// change min index to search upper subarray
imin = imid + 1;
else
// change max index to search lower subarray
imax = imid - 1;
}
// key was not found
return KEY_NOT_FOUND;
}
And here is a version of your code that is far from bug-free but does what you intended. You weren't so far off
use strict;
use warnings 'all';
print binarysearch( 76, 10 .. 99 ), "\n";
sub binarysearch {
my $numbertolook = shift;
my #intarray = #_;
my $lengthint = scalar #intarray;
my #sorted = sort { $a <=> $b } #intarray;
my $low = 0;
my $high = $lengthint - 1;
my $found = undef;
my $midpoint;
while ( $low < $high ) {
$midpoint = int( ( $low + $high ) / 2 );
#print $midpoint, " ",$low," ", $high, " ", #sorted, "\n";
if ( $numbertolook < $sorted[$midpoint] ) {
$high = $midpoint;
}
elsif ( $numbertolook > $sorted[$midpoint] ) {
$low = $midpoint;
}
elsif ( $numbertolook == $sorted[$midpoint] ) {
$found = 1;
print "FOUND\n";
return $midpoint;
}
if ( $low == $high - 1 and $low == $midpoint ) {
if ( $numbertolook == $sorted[$high] ) {
$found = 1;
print "FOUND\n";
return $midpoint;
}
return;
}
}
return $midpoint;
}
output
FOUND
66
If you call print with several parameters separated with a space print expects the first one to be a filehandle. This is interprented as print FILEHANDLE LIST from the documentation.
print $found "\n";
What you want to do is either to separate with ,, to call it as print LIST.
print $found, "\n";
or to concat as strings, which will also call it as print LIST, but with only one element in LIST.
print $found . "\n";

Can someone explain the logic of the recursion code and when it's best to use?

I'm learning perl and I want to understand the logic better so I can improve in programming. I was wondering if anyone could explain it part by part. I think I have a good grasp of what's happening instead of this line $num = $val * fact($val-1); ?
#!/usr/bin/perl
use warnings;
use strict;
print "Enter in a number\n";
my $input = <>;
my $num = fact($input);
print "The factorial of $input is $num\n";
sub fact {
my $val = $_[0];
if ( $val > 1 ) {
$num = $val * fact( $val - 1 );
}
else {
$num = 1;
}
}
exit;
The first line is the shebang, which specifies which version of Perl to use.
#!/usr/bin/perl
The next two lines will help you catch mistakes in your program and make sure you are coding properly. See Why use strict and warnings?
use warnings;
use strict;
print will print the message in quotes.
print "Enter in a number\n";
The diamond operator, <>, used in this context, is the same as calling readline. It will read the input from STDIN.
my $input=<>;
The next line is calling the subroutine fact with $input as an argument.
my $num= fact($input);
Printing the result. $input and $num will be interpolated because you are using double quotes.
print "The factorial of $input is $num\n";
Finally, the part you are most interested in.
sub fact{
my $val = $_[0];
if ($val > 1) {
$num = $val * fact($val-1);
} else {
$num = 1;
}
}
The first line of this subroutine my $val = $_[0];, is setting $val equal to the value you call it with. The first time through, you call is with $input, so $val will be set to that value.
Next, we have this if else statement. Suppose you enter 5 on the command line, so $input was 5. In that case, it is greater than 1. It will execute the statement $num = $val * fact($val-1);. Seeing as the value of $val is 5, it would be the same as calling $num = 5 * fact(4);.
If we were going to continue looking at the what code is executing, you'll see that now we are calling fact(4);. Since 4 > 1 it will pass the if statement again, and then call fact(3).
Each time we are multiplying the number by that number minus one, such as $val = 5 * 4 * 3 * 2 * 1.
From perlsub
If no return is found and if the last statement is an expression, its
value is returned. If the last statement is a loop control structure
like a foreach or a while , the returned value is unspecified. The
empty sub returns the empty list.
So this is why we don't have to return $num at the end of your fact subroutine, but it may be useful to add to increase readability.
Just to break down what this is doing.
$num = 5 * fact(4);
fact(4) is equivalent to 4 * fact(3).
$num = 5 * (4 * fact(3));
fact(3) is equivalent to 3 * fact(2).
$num = 5 * (4 * (3 * fact(2)));
fact(2) is equivalent to 2 * fact(1).
$num = 5 * (4 * (3 * (2 * fact(1)));
fact(1) is equivalent to 1.
$num = 5 * (4 * (3 * (2 * 1));
Search recursion on Google for another example (did you mean recursion?).
As a wise man once said: "To understand recursion, you must first understand recursion."
Anyway - there are a bunch of algorithms that can work recursively. Factorial is one.
A factorial of 5! = 5*4*3*2*1. This makes it quite a good case for recursion, because you could also say it's 5 * 4!. This is what the code is doing. When you supply a number to the subroutine 'fact' it calculates the factorial of one number lower, then multiplies by the original number. Except when it gets a value of 1 or less.
So give your fact "3" to start off. (same applies to bigger numbers, but the example is longer!)
It sets val to '3'.
Then, because '3 > 1' it goes and gets 'fact(2)'.
which because 2 > 1, goes and runs 'fact(1)'.
which because it isn't >1, returns '1'.
which is returned to the 'fact(2)' sub, multiple by '2' (giving 2) and returned as a result
to the 'fact(3) sub, which multiplies the returned result by 3, to give 6.
Personally I find recursion is a good way to confuse anyone who's reading your code. It's suitable for problems that are implicitly recursive - such as factorials, fibonnaci sequences and directory traversals - and generally should be avoided otherwise.
The reason you're having trouble learning from that code is because it's poorly designed:
The subroutine needlessly uses a lexical variable ($num) from outside the subroutine. Don't do this!
The subroutine relies on implied return values instead of specifying return explicitly.
Fixing these issues clarifies the functionality a lot:
sub fact {
my $val = $_[0];
if ( $val > 1 ) {
return $val * fact( $val - 1 );
}
else {
return 1;
}
}
And then using a ternary to reduce more:
sub fact {
my $val = shift;
return $val > 1 ? $val * fact( $val - 1 ) : 1;
}
As for when recursion is good to use? The answer is when you need it.
The factorial is an obvious example of where recursion could be used, but it's better to avoid using it when one has a choice. This is for both readability and functional reasons:
sub fact {
my $val = shift;
my $fact = 1;
while ($val > 1) {
$fact *= $val--;
}
return $fact;
}

Perl - Returning the maximum value in a data set

I have never delved into the world of Perl before and I find it pretty confusing and could use some help. In the code below the calc() section returns a running average of an'input' over 'count' samples. I would like to modify that so the calc() returns the maximum value within the sample set. Thanks in advance for the help!
sub calc
{
my ($this, $dim, $input, $count) = #_;
if ($count < 1)
{
warn "count=$count is less than 1.";
return undef;
}
my $inputsum_in = $this->{inputsum};
my ($inputcumsum, $inputsum_out) = PDL::CumulativeSumOver2($input, $inputsum_in);
my $inputdelay = $this->delay('inputhistory', $input);
my $inputdelaysum_in = $this->{inputdelaysum};
my ($inputdelaycumsum, $inputdelaysum_out) = PDL::CumulativeSumOver2($inputdelay, $inputdelaysum_in);
$this->{inputsum} = $inputsum_out;
$this->{inputdelaysum} = $inputdelaysum_out;
my $sampleno = $this->{sampleno};
my $divider = $count;
if($sampleno < $count)
{
my $last = $dim - 1;
$divider = sequence($dim) + ($sampleno + 1);
my $start = $count - $sampleno;
$divider->slice("$start:$last") .= $count if $start <= $last;
$this->{sampleno} = $sampleno + $dim;
}
return ($inputcumsum - $inputdelaycumsum) / $divider;
}
How about
$max = max($input);
PDL Primitives
If you want to find the maximum of a certain list of values, you do not need to write your own subroutine. There is already a function that comes shipped with perl v5.7.3 or higher:
use List::Util qw(max); # core module since v5.7.3
use strict;
use warnings;
print max(1 .. 10); # prints 10
EDIT: Here is the loop I take it you need.
Read input data from sensor
append new data to stored data
Throw away excess data
Evaluate
Here's how I'd do it.
my $storedData = pdl;
# $storedData is now a vector containing one element, 0
while (! stopCondition()) {
my $input = readSensorData(); # step 1
$storedData = $storedData->append($input); # step 2
if ($storedData->nelem > $count) { # step 3
$storedData = $storedData->slice("-$count:-1");
# note that -1 points to the last element in a piddle and -X refers to
# the element X-1 away from the end (true for piddles and native arrays)
}
my ($max, $min) = evaluate($storedData); # step 4
}
I'm not sure if this answers your question, but your comment below seems pretty different from the question you have above. Consider editing the above to better reflect what you're having trouble with or asking a new question.
An easy way to get a running average is with a finite impulse response filter, aka convolution. Convolve any signal with a (normalized) rectangular impulse and you get running average.
my $filter = ones($count) / $count;
my $runningAve = convolveND($input, $filter);
my $max = $runningAve->max`;
Or in one line
my $max = convolveND($input, ones($count) / $count)->max;
convolveND is documented here.
There is one thing to be careful of with this method, which is that the values at the beginning and end of the $runningAve piddle aren't really running averages. To ensure that the output is the same size as the input convolveND (by default) effectively concatenates zeroes to the beginning and end of the input, the result being that the first and last few elements of $runningAve are lower than actual running averages. (Note that a running average should have N - (window - 1) elements in principle, N being the size of $input.) Since these "bad" values will necessarily be lower than the actual running average values, they won't disturb the maximum that you want. (Re "by default": convolveND has other ways of handling edges, as you will see in the documentation linked to above.)
(NB: I am not a PDL expert. There may be a cheaper way to get the running average that's cheaper than convolveND, something like $ra = $input->range(...)->sumover(0) / $count, but I don't know what you'd put in the ... and the above is readable. See also http://search.cpan.org/~jlapeyre/PDL-DSP-Iir-0.002/README.pod#moving_average)

When is the spaceship operator used outside a sort?

I've only seen the Perl spaceship operator (<=>) used in numeric sort routines. But it seems useful in other situations. I just can't think of a practical use.
What would be an example of when it could be used outside of a Perl sort?
This is a best practice question.
I'm writing a control system for robot Joe that wants to go to robot Mary and recharge her. They move along the integer points on the line. Joe starts at $j and can walk 1 meter in any direction per time unit. Mary stands still at $m and can't move -- she needs a good recharge! The controlling program would look like that:
while ($m != $j) {
$j += ($m <=> $j);
}
The <=> operator would be useful for a binary search algorithm. Most programing languages don't have an operator that does a three-way comparison which makes it necessary to do two comparisons per iteration. With <=> you can do just one.
sub binary_search {
my $value = shift;
my $array = shift;
my $low = 0;
my $high = $#$array;
while ($low <= $high) {
my $mid = $low + int(($high - $low) / 2);
given ($array->[$mid] <=> $value) {
when (-1) { $low = $mid + 1 }
when ( 1) { $high = $mid - 1 }
when ( 0) { return $mid }
}
}
return;
}
In any sort of comparison method. For example, you could have a complicated object, but it still has a defined "order", so you could define a comparison function for it (which you don't have to use inside a sort method, although it would be handy):
package Foo;
# ... other stuff...
# Note: this is a class function, not a method
sub cmp
{
my $object1 = shift;
my $object2 = shift;
my $compare1 = sprintf("%04d%04d%04d", $object1->{field1}, $object1->{field2}, $object1->{field3});
my $compare2 = sprintf("%04d%04d%04d", $object2->{field1}, $object2->{field2}, $object2->{field3});
return $compare1 <=> $compare2;
}
This is a totally contrived example of course. However, in my company's source code I found nearly exactly the above, for comparing objects used for holding date and time information.
One other use I can think of is for statistical analysis -- if a value is repeatedly run against a list of values, you can tell if the value is higher or lower than the set's arithmetic median:
use List::Util qw(sum);
# $result will be
# -1 if value is lower than the median of #setOfValues,
# 1 if value is higher than the median of #setOfValues,
# 0 if value is equal to the median
my $result = sum(map { $value <=> $_ } #setOfValues);
Here's one more, from Wikipedia: "If the two arguments cannot be compared (e.g. one of them is NaN), the operator returns undef.", i.e., you can determine if two numbers are a a number at once, although personally I'd go for the less cryptic Scalar::Util::looks_like_number.