【问题标题】:How can I ask the user to re-enter input when they enter invalid input, in Perl?在 Perl 中输入无效输入时,如何要求用户重新输入输入?
【发布时间】:2009-09-29 06:51:42
【问题描述】:

我有一个 Perl 子例程,它询问用户的输入。我在该子程序本身内部执行检查输入的输入是否为有效输入。

如果不是,我想再次调用子程序,让用户这次输入有效的输入。

我的子程序如下:

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

那么如何在这个中递归一个子程序呢?

【问题讨论】:

  • 为什么要追索?也许是一个while循环?
  • 当我使用 some_routine();递归没有任何反应(它不会返回要求用户再次输入以进行选择)......我该怎么办?
  • 大家好...感谢您的回答...。我的上述评论是由于以下原因造成的:::::: ==> 当我给出值时 "y" 。 .. $choice 变为 "y\n" 并且它不会进入 if sttmnt 因为它附加了一个换行符...在这种情况下递归子例程 :: some_routine(); sttmnt 就足够了:)
  • 您发布到 Using Perl5 in Web Programming 的链接是一本非常古老(1996 年)、非常盗版的书。不要出于这两个原因使用这本书。有很好的、合法的、作者认可的资源,比如hop.perl.plover.com,所有的书都在perl.org/books/library.html

标签: perl recursion subroutine


【解决方案1】:

要在 Perl 中递归调用子例程,您只需从其自身调用 sub,就像在任何其他语言中一样:

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

但是,您并不想将递归用于“重复直到条件改变”的场景。
这就是 while 循环的用途
:

my $valid;
while (!$valid) {
  print "Enter something: ";
  my $data = <STDIN>;
  $valid = validate($data);
  print "Bzzt!  Invalid - try again!\n" unless $valid;
}

【讨论】:

  • 虽然这也是我的第一个想法,但这是 Perl:有不止一种方法可以做到。
  • @Jurily:有不止一种方法可以做到这一点,但这并不意味着它们在所有情况下都同样适用。
  • TMTOWTDI, BMOTWAW。 方法不止一种,但大多数方法都是错误的。
  • 我会这样做,即使我的代码看起来有点不同。子程序应该做一件事,你不应该在其中隐藏程序流。在这段代码中,所有的流程都在你面前。
  • 嘿,我想发明一个新的首字母缩写词:TMTOWTDI, BLSOO。有不止一种方法可以做到这一点,但让我们标准化一种。
【解决方案2】:

没有理由为此使用递归。一个简单的while 循环就可以了。

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

如果validate_input 返回 0,则循环将重复。

【讨论】:

  • 几乎任何(或者,更确切地说是所有)递归代码都可以通过不同程度的努力被重写为循环:)
【解决方案3】:

递归

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 - 尾调用优化 (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;
}

或重做

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

等等……

【讨论】:

  • 您不需要对 STDIN 进行两次调用。 :)
  • WAT?这是避免递归调用使堆栈重载的技巧吗?
  • 是的,转到(和重做)版本将防止堆栈过载。
【解决方案4】:

调用例程的正确方法是

goto &some_routine;

...因为你有一个尾调用 - 这是你在函数中做的最后一件事。如果您正常调用它,则每次调用都会占用一个堆栈帧,并且内存分配会增加。像这样调用,你重复使用相同的堆栈帧。从你作为程序员的角度来看,这和

return some_routine(@_);

但不吃记忆。

这仅适用于称自己为最后一件事的例程 - 在其他情况下,您确实应该切换到其他人建议的 while 循环(并且,为了代码的吸引力,您无论如何可能都想这样做)。

【讨论】:

  • 这里不需要goto。
  • Brad: some_routine() 是正确的,因为他的原始调用没有参数。您的编辑 (some_routine(@_)) 适用范围更广,但此处实际上并不需要。
  • brian:那么 TCO 默认工作?如果是这样,那么显然这里的大多数答案都有同样的问题,这被广泛误解了......
【解决方案5】:

编辑:出于多种原因(风格、性能等),我强烈建议不要在这里进行递归调用,而是在while 循环。

[原始回答者的免责声明]“从风格的角度来看,我不会在这里进行递归调用,而是在 while 循环中检查它,但我想在某种程度上,这也是一个品味问题。”

就使用递归而言,例如,您可以从函数内部调用函数,如下所示:

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

【讨论】:

    【解决方案6】:
    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;     
        }
    }
    

    【讨论】:

    • 感谢编辑,我不敢相信我错过了这些。这就是我没有在突出显示的文本编辑器中输入的结果。
    【解决方案7】:

    使用IO::Prompt 模块。

    有了它,你可以这样写:

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

    【讨论】:

      【解决方案8】:

      您的输入不是 eq "y" 而是 "y\n"。

      如果您将行更改为if ($choice =~ /^[Yy]/),这将确保您在输入的开头捕捉到 Y,而不必担心 y 或 yes 或 Y 或 Yes。

      作为帮助,您应该使用&lt;STDIN&gt; 而不是单独使用STDIN。始终在顶部添加use strict; use warnings;。这确保您需要使用以下方法定义 $value 和 $choice:

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

      正如其他人所提到的。作为一个循环,这可能更直接。

      #!/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;
              }
          }
      }
      

      【讨论】:

        【解决方案9】:

        我使用的是一个简单的 goto:

        START:
        print "\n Name of the country (any one out of: china, japan or tokyo): ";
        my $country_name = <>;
        chomp($country_name);
        
        unless ($country_name eq "china" || $country_name eq "japan" ||
            $country_name eq "tokyo") {
            print "\n Invalid country name entered. Please try again. \n";
            goto START;
        }
        

        这是一种非常幼稚的方法,但适用于初学者。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-06-20
          • 1970-01-01
          • 2021-08-25
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多