views:

397

answers:

8

Hi,

I've a Perl subroutine which asks input from User. I perform a check inside that subroutine itself whether the input entered is a valid input.

If it's not, I want to call the subroutine again to let the user enter a valid input this time.

My subroutine is as follows:

sub some_routine {    
    print "Enter a number to select   (1) Apple (2) Mango (3) grapes:"
    $value=STDIN;
    if($value =~ /[^1-3]/ ) {
        print "The input is not valid!";
        print "Do you want to continue selecting a fruit again (Y or N)?";
        $choice = STDIN;
        if( $choice eq "y") {
            ### I want to call the subroutine again to enter input ###
          } else {
            exit;
        }
    }
}

So how to recurse a subroutine in this?

+2  A: 

EDIT: For a variety of reasons (style, performance, etc...), I would strongly advise not do a recursive call here, though, but rather check it in a while loop.

[Original answerer's disclaimer] "From a style perspective, I would not do a recursive call here, though, but rather check it in a while loop, but I guess to a degree, that's a matter of taste as well."

As far as using recursion, as an example, you can just call the function from within the function, like so:

sub get_positive_number {
    print "Please enter a positive number: ";
    my $number = <STDIN>;
    chomp $number;
    if ($number > 0) {
        return $number;
    }
    else {
        return get_positive_number();
    }
}

my $result = get_positive_number();
print "result: $result\n";
alech
Performance? With user input? ROFLAMO!
Hynek -Pichi- Vychodil
+7  A: 

There's no reason to use recursion for this. A simple while loop will do.

my $input_valid = 0;
while( !$input_valid ) { 
    print "Enter some input: ";
    my $input = <STDIN>;
    $input_valid = validate_input( $input );
}

If validate_input returns 0, the loop will repeat.

friedo
Pretty much any (or, rather all) recursive code can be, with varying degrees of effort, re-written as a loop :)
DVK
+9  A: 

To call a subroutine recursively in Perl, you just call the sub from itself, the same as in any other language:

sub factorial {
  my $num = shift;
  return 1 if $num < 2;
  return $num * factorial($num - 1);
}

However, you don't really want to use recursion for a "repeat until condition changes" scenario.
That's what while loops are for
:

my $valid;
while (!$valid) {
  print "Enter something: ";
  my $data = <STDIN>;
  $valid = validate($data);
  print "Bzzt!  Invalid - try again!\n" unless $valid;
}
Dave Sherohman
While was my first thought too, but this is Perl: There's More Than One Way To Do It.
Jurily
@Jurily: there's more than one way to do it, but that doesn't mean that they're all equally good for all cases.
Telemachus
*TMTOWTDI, BMOTWAW.* There's More Than One Way To Do It, But Most Of Those Ways Are Wrong.
Adam Bellaire
This is the way I would do it, even if my code looks a bit different. Subroutines should do their one thing, and you shouldn't hide program flow in them. In this code, all the flow is right there in front of you.
brian d foy
Hey, I want to invent a new acronym: *TMTOWTDI, BLSOO*. There's More Than One Way To Do It, But Lets Standardize On One.
R. Bemrose
Shouldn't the 'print "Enter something: ";' be inside the loop?
lamcro
@lamcro: Good point. I've moved it inside the loop.
Dave Sherohman
+2  A: 
my $value;
until(defined $value = get_value()) {
  print"you didn't enter a valid value\n";
}

sub get_value {
 print "Enter a number to select   (1) Apple (2) Mango (3) grapes:"
    $value=<STDIN>;
    if($value =~ /[1-3]/ ) {
        return $value;
    } else {
        return undef;     
    }
}
Ape-inago
thanks for the edit, I can't believe I missed those. Thats what I get for not typing this up in a highlighting text editor.
Ape-inago
+4  A: 

The correct way to call the routine is

goto &some_routine;

...because what you have is a tail call - it's the last thing you do in your function. If you call it normally you eat a stack frame for each call and memory allocation goes up. Called like this, you re-use the same stack frame. From your perspective as a programmer, this is the same as

return some_routine(@_);

but without eating memory.

This only works for routines that call themselves as the last thing they do - in other cases, you should indeed be switching to the while loop that other people suggest (and, for code attractiveness, you may want to do that anyway).

ijw
+1 for a good use of goto.
Dave Sherohman
There's no need for the goto here.
brian d foy
Brad: some_routine() was correct because his original call has no arguments. Your edit (some_routine(@_)) is more widely applicable but not actually required here.
ijw
brian: TCO works by default then? If so, then apparently most of the answers here have the same problem and this is widely misunderstood...
ijw
+4  A: 

recursive

sub select_fruit {    
    print "Enter a number to select   (1) Apple (2) Mango (3) grapes:"
    $value=<STDIN>;
    if($value =~ /[^1-3]/ ) {
        print "The input is not valid!";
        print "Do you want to continue selecting a fruit again (Y or N)?";
        $choice = <STDIN>;
        if( $choice eq "y") {
            $value = select_fruit();
          } else {
            exit;
        }
    }
    return $value;
}

goto - Tail Call Optimization (TCO)

sub select_fruit {
    print "Enter a number to select   (1) Apple (2) Mango (3) grapes:"
    $value=<STDIN>;
    if($value =~ /[^1-3]/ ) {
        print "The input is not valid!";
        print "Do you want to continue selecting a fruit again (Y or N)?";
        $choice = <STDIN>;
        if( $choice eq "y") {
            goto &select_fruit;
          } else {
            exit;
        }
    }
    return $value;
}

or redo

sub select_fruit {
SELECT_FRUIT: {
       print "Enter a number to select   (1) Apple (2) Mango (3) grapes:"
       $value=<STDIN>;
       if($value =~ /[^1-3]/ ) {
           print "The input is not valid!";
           print "Do you want to continue selecting a fruit again (Y or N)?";
           $choice = <STDIN>;
           if( $choice eq "y") {
               redo SELECT_FRUIT; # same as goto SELECT_FRUIT;
             } else {
               exit;
            }
        }
        return $value;
    }
}

and so ...

Hynek -Pichi- Vychodil
You don't need to have the two calls to STDIN. :)
brian d foy
Copy paste and it serves me right :(
Hynek -Pichi- Vychodil
WAT?is that a hack to avoid the recursive calls overloading the stack?
Ape-inago
Yes, goto (and redo) version will prevent overloading the stack.
Hynek -Pichi- Vychodil
+1  A: 

Use IO::Prompt module.

With it you can write it like this:

use IO::Prompt;
my @choices = qw( Apple Mango Grapes );
my $answer = prompt("Select :", "-menu" => \@choices);
print $answer;
depesz
A: 

Your input is not eq "y" but "y\n".

If you change the line to if ($choice =~ /^[Yy]/) this will make sure you catch the Y at the start of the input and not worry about y or yes or Y or Yes.

As a help, you should use <STDIN> instead of STDIN alone. Always add use strict; use warnings; at the top. This makes sure you need to define $value and $choice using:

my $value = '';
my $choice = '';

As other people have mentioned. This is probably more straightforward as a loop.

#!/usr/bin/env perl
use strict;
use warnings;

some_routine();

sub some_routine {    
    my $value = '';
    my $choice = '';
    print "Enter a number to select   (1) Apple (2) Mango (3) grapes:";
    $value = <STDIN>;
    if($value !~ /[1-3]/ ) {
        print "The input is not valid!";
        print "Do you want to continue selecting a fruit again (Y or N)?";
        $choice = <STDIN>;
        if( $choice =~ /[Yy]/) {
            some_routine();
          } else {
            exit;
        }
    }
}
Xetius