【问题标题】:While-loop infinite when wrong input is entered输入错误时循环无限
【发布时间】:2018-10-13 15:17:06
【问题描述】:

我正在创建一个包含 4 个人姓名的哈希。我希望用户使用其中一个名称并获得该专业。同时我希望程序在名称输入错误的情况下继续要求输入名称。但是,当我运行我的代码时,当找不到名称时,它会进入无限循环。任何线索如何解决这个问题?

use strict;
use warnings;

sub main {

my %profession = (
    "Emelie"  => "Economist",
    "Hugo" => "Scientist",
    "Maria"  => "Accountant",
    "Linnéa"  => "Medical Doctor",
    );

print ("Enter first name: ");
chomp(my $name = <STDIN>);

my $var = 1;
while ($var) {
    if (exists $profession{$name}) {
        print "The profession is: ", $profession{$name}, "\n";
        $var = 0
    }
    else {
        print "No such name found :-(, try once again\n";
    }

}

}
main();

无限循环给出了这个:

找不到这样的名字:-(,再试一次

找不到这样的名字:-(,再试一次

找不到这样的名字:-(,再试一次

找不到这样的名字:-(,再试一次

然后继续……

提前致谢:-)

【问题讨论】:

  • 您永远不会重新分配 $name 的值,这意味着 $var 永远不能是 1 以外的任何值,其计算结果为 true。

标签: perl scripting-language


【解决方案1】:

您的代码中存在逻辑错误。出于演示的目的,我将从使用STDIN 作为您的输入句柄切换到使用DATA 的位置,以便我可以烘焙一些测试数据。我还将删除外部子程序,因为它与讨论无关。

use strict;
use warnings;

my %profession = (
    Emelie    => 'Economist',
    Hugo      => 'Scientist',
    Maria     => 'Accountant',
    Linnéa    => 'Medical Doctor',
);

chomp(my $name = <DATA>);

my $var = 1;
while($var) {
    if (exists $profession{$name}) {
        print "The profession for $name is $profession{$name}\n";
        $var = 0;  # This could just be the 'last' keyword.
    }
    else {
        print "No such name ($name) found. Try again.\n";
    }
}

__DATA__
John
Dave
Maria

正如您从示例数据中看到的那样,您会期望具有正确逻辑的程序在第三次尝试后终止,因为Maria 是您的哈希键之一。但如果你运行它,你会看到:

No such name (John) found. Try again.
No such name (John) found. Try again.
No such name (John) found. Try again.
No such name (John) found. Try again.
No such name (John) found. Try again.
No such name (John) found. Try again.
No such name (John) found. Try again.
No such name (John) found. Try again.
^C
Command terminated

^CCommand terminated 行来自我最终点击 -c 终止运行时。)

此时,通过打印最近从&lt;DATA&gt; 读取的名称的附加信息,很容易看出我们只在查看John。但为什么?因为您的循环不会从 &lt;DATA&gt; 文件句柄中读取。你是在循环之外做的。

这里有一个更 Perlish 的方式来完成你想做的事情:

use strict;
use warnings;

my %profession = (
    'Emelie'    => 'Economist',
    'Hugo'      => 'Scientist',
    'Maria'     => 'Accountant',
    'Linnéa'    => 'Medical Doctor',
);

while(my $name = <DATA>) {
    chomp $name;

    if (exists $profession{$name}) {
        print "The profession for $name is $profession{$name}\n";
        last;
    }

    print "No such name ($name) found. Try again.\n";
}

__DATA__
John
Dave
Maria

现在输出将是:

No such name (John) found. Try again.
No such name (Dave) found. Try again.
The profession for Maria is Accountant

第一次通过这个while循环我们从DATA读取并获得John\n。我们将它分配给$namechomp 它,然后检查John 是否作为哈希键存在。它没有,所以我们打印名称并继续下一次迭代。在第二次迭代中,&lt;DATA&gt; 读取 Dave\n,将其切碎并检查它是否存在。它没有,所以我们打印名称并继续下一次迭代。

在第三次迭代中,我们&lt;DATA&gt; 获得Maria\n,将其切碎,并检查是否存在哈希键Maria。它确实如此,因此它打印与该键关联的值,然后点击last 语句。 last 告诉流控制立即退出封闭循环。循环主块中的其余行被跳过,不再有迭代。它通常比示例代码中的 $var 等标记变量更易读,因为代码的读者无需跟踪变量可能处于的状态。

简而言之,你的错误是只从输入文件句柄中读取一次,然后期望你的循环在进入循环之前遇到$name 的变化,尽管它只被分配了一次。解决方案是将读取的文件移动到循环中。

此模式记录在 perldoc perlvar 中,我鼓励您花几分钟时间阅读以更好地熟悉该语言。

更新:我确实看到了一个答案,其中再次调用 main() 用作迭代读取的文件的一种方式,这并没有错。但是在真正的脚本中main() 可能会变得更大,当这种情况发生时,文件读取循环要么需要再次成为显式循环,要么被分解为可以调用自身的不同子例程。此外,while 循环方法是一种惯用或常见的解决方案,更有可能在其他人编写的代码中找到。使用递归读取文件是一种不太常见的模式。

【讨论】:

  • 非常感谢。很好的解释(从来没有想过使用 DATA 或“last”,因为我是这种语言的新手)。会竖起大拇指,但必须坚持使用@Gilles 的递归函数。
【解决方案2】:

使用这个,简化,不需要循环(递归函数):

use strict;
use warnings;

sub main {
    my %profession = (
        "Emelie"  => "Economist",
        "Hugo" => "Scientist",
        "Maria"  => "Accountant",
        "Linnéa"  => "Medical Doctor",
    );

    print ("Enter first name: ");
    chomp(my $name = <STDIN>);

    if (exists $profession{$name}) { 
        print "The profession is: ", $profession{$name}, "\n";
    }
    else {
        print "No such name found :-(, try once again\n";
        main();
    }
}
main();

【讨论】:

  • 没有注意到您删除了 while 循环。这是最好的解决方案。谢谢!
【解决方案3】:

只有在找到名称时才重置 $var。

移动这条线...

$var = 0

在 if/else 之后。

你还需要移动这条线...

chomp(我的 $name = );

在 while 循环内,在 if 之前。

【讨论】:

  • 但是如果 $var = 0 在 if/else 之后,如果条件落在 "else" 中,那么如果用户再次输入错误的名称,它只会询问一次,然后停止 while 执行。这个想法是让 while 运行直到输入正确的匹配项。
  • 将接受@Gilles 解决方案,因为它效果最好!不过谢谢:-)
【解决方案4】:

第 20 行,一个“;”不见了

它不起作用,因为您需要在找不到名称时再次询问名称,否则它将循环($var 不会更改为 0)

简单示例:

else{
    print "No such name found :-(, try once again\n";
    chomp($name = <STDIN>);
}

【讨论】:

  • 第 20 行不需要分号。
猜你喜欢
  • 1970-01-01
  • 2012-09-28
  • 2013-07-26
  • 2021-12-20
  • 2021-03-12
  • 2019-03-05
  • 2019-06-15
  • 1970-01-01
  • 2012-10-18
相关资源
最近更新 更多