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

Folks, Just a quick question, in below script if user input is "Enter key" then I can't return to main menu. It defaults to condition 3 (I don't understand why)?
Can you please suggest me how can I detect an Enter key. I was trying to use Switch fuction but its not working in PERL V 5.8.4. Appreciate your help.
#!/usr/bin/perl local ($bak_id)=shift; local ($acc_bal)=shift; $bak_id=<STDIN>; $acc_bal=<STDIN>; chomp ($bak_id); print "bak_id = $bak_id\n"; print "acc_bal = $acc_bal\n"; if (($acc_bal < 1.0 ) && ($bak_id eq "BOTH")) { print "condition 1\n"; } elsif (($acc_bal > 1.0 )&&($bak_id eq "BOTH")){ print "condition 2\n"; } elsif (($acc_bal < 1.0 ) && ($bak_id eq ("Cust1")||("Cust2"))) { print "condition 3\n"; } elsif (($acc_bal > 1.0 ) && ($bak_id eq ("Cust1"||"Cust2"))) { print "condition 4\n"; local ($choice); $choice=<STDIN>; if ($choice="YES") { print "condition 4.1\n" } elsif ($choice!="YES") { print "condition 4.2\n"; } } elsif (($bak_id eq "\n") || ($acc_bal eq "\n")) { print "return to MM\n"; } else { print ("Returning to Main Menu\n"); }

Replies are listed 'Best First'.
Re: Detecting a Null or Enter key
by swampyankee (Parson) on Jan 11, 2009 at 14:32 UTC

    From a quick scan, if the user presses the <enter> key, s?he will enter a newline ("\n"), which Perl will interpret as 0 in the numerical equality test, and 0 is less than 1.0, so the result for the first part of condition 3 is "TRUE." The second part of the test for condition 3 is ill-formed, in that "Cust1"||"Cust2" is legitimate, albeit useless, Perl, which will return true if "Cust1" or "Cust2" is true, which they are given Perl's notion of truth. You should probably rewrite that part of the test to

    $bak_id eq 'Cust1' or $bak_id eq 'Cust2'
    or use a regex:

    $bak_id =~ /^Cust[12]$/
    in both places it occurs. Actually, since you're using the same sort of test twice, I'd move the logic into a named sub against the inevitable day when the customer (instructor?) generalizes Cust[12] to /Cust[0-9]+/.

    Since this test is before the tests to return to main menu, it never gets there.


    Addendum There are many ways to code the logic you need to validate input, including some modules. My general preference, in Perl, is to assume all inputs from stdin are strings, and postpone any numerical tests until after I've ensured myself that the data actually represent legitimate numbers.


    Information about American English usage here and here. Floating point issues? Please read this before posting. — emc

Re: Detecting a Null or Enter key
by Bloodnok (Vicar) on Jan 11, 2009 at 14:54 UTC
    IMO, you need to use strictures, after which the test for empty input should be performed first ... and then go on to process the entered data e.g.
    #!/usr/bin/perl use warnings; use strict; local ($bak_id)=shift; local ($acc_bal)=shift; $bak_id=<STDIN>; $acc_bal=<STDIN>; chomp ($bak_id); print "Returning to MM\n", exit 0 unless $bak_id; . . .
    Even better, IMO, would be to use a hash-based dispatch table keyed on (what was previously) $bak_id expressed as a regex e.g.
    #!/usr/bin/perl use warnings; use strict; my %dispatch = ( '' => sub { print "Returning" ; exit }, 'BOTH' => sub { my $acc_bal = shift; . if ($acc_bal < 1.0) { . . } else { . . } }, 'Cust1|Cust2' => sub { my $acc_bal = shift; . if ($acc_bal < 1.0) { . . } else { . . } }, ); my ($bak_id, $acc_bal); while (1) { chomp($acc_bal = <STDIN>); foreach my $re (keys %dispatch) { next unless $acc_bal =~ /^(?:$re)$/; &{ $dispatch{$_} }(chomp <STDIN>); } } . .
    or some such.

    Afterthought: I've only just realised that you don't allow a value of 0 - it's either less or grater than 0 ?

    A user level that continues to overstate my experience :-))
      local ($bak_id)=shift; local ($acc_bal)=shift;

      won't pass strict. But that's good because it's inappropriate to use local here. The above should be replaced with

      my ($bak_id)=shift; my ($acc_bal)=shift;

      In both cases, the parens are useless.

      my $bak_id = shift; my $acc_bal = shift;

      Or you could combine them

      my ($bak_id, $acc_bal) = @ARGV;
        You may notice that the 1st suggestion utilised a snippet from the OP (tho' I take your point that it was remiss of me to insert the strictures) - my 2nd suggestion deliberately didn't repeat the errror.

        A user level that continues to overstate my experience :-))
Re: Detecting a Null or Enter key
by swampyankee (Parson) on Jan 11, 2009 at 17:41 UTC

    Also, I've noticed that you had said you had used Dr Conway's Switch syntax. Himself has said don't do that, in this posting.


    Information about American English usage here and here. Floating point issues? Please read this before posting. — emc

Re: Detecting a Null or Enter key
by cdarke (Prior) on Jan 12, 2009 at 10:48 UTC
    What happens if $acc_bal is exactly 1.0? You are doing a chomp on $bak_id, so it can never be "\n" (unless you changed $/). In might also be a good idea to chomp $acc_bal, if only for consistency.

    I was trying to use Switch fuction but its not working in PERL V 5.8.4

    Yes, pity Switch was put into the base. The switch/case statement has had a chequered history. There is a new one in 5.10, but for now:
    #!/usr/bin/perl use strict; use warnings; my $bak_id; my $acc_bal; chomp($bak_id =<STDIN>); chomp($acc_bal=<STDIN>); SWITCH: { (!$bak_id or !$acc_bal) && do { print "Returning to Main Menu\n"; return; # or somesuch }; ($acc_bal < 1.0 && $bak_id eq "BOTH") && do { print "condition 1\n"; last SWITCH }; ($acc_bal > 1.0 && $bak_id eq "BOTH") && do { print "condition 2\n"; last SWITCH }; ($acc_bal < 1.0 && $bak_id =~ /^Cust[12]$/) && do { print "condition 3\n"; last SWITCH }; ($acc_bal > 1.0 && $bak_id =~ /^Cust[12]$/) && do { print "condition 4\n"; # blah, blah, blah last SWITCH }; print "Invalid entry\n"; }