3

我对 Perl 很陌生,并且被分配了一个简单的猜谜游戏,用户有 8 次机会猜测 1 到 100 之间的数字。我不断收到上面提到的错误,无法弄清楚。

这是我的代码:

    use Modern::Perl;

    my ($guess,$target,$counter); 
    $target = (int rand 100) + 1;

    while ($guess < $target)
    {
        chomp ($guess=<>);
        print "Enter guess $counter: ";

        $counter++;

        if ($guess eq $target) {
            print "\nCongratulations! You guessed the secret number $target in   $counter";
        }
        elsif ($guess > $target) {
            print "\nYour guess, $guess, is too high.";
        }
        elsif ($guess < $target) {
            print "\nYour guess, $guess, is too low.";
        }
        else {
            print "You lose. The number was $target.";
        }
    }
4

3 回答 3

3

您的代码存在一些问题。这是我的代码,使用不同的方法:

#!/usr/bin/perl

use 5.012;   # use strict; use feature 'say';
use warnings;

my $number = (int rand 100) + 1;
my $max_guesses = 8;

GUESS: foreach my $guess_no (1..$max_guesses) {
  say "($guess_no) Please enter a guess:";
  my $guess = <>;
  chomp $guess;

  unless ($guess =~ /^\d+$/) {
    say "Hey, that didn't look like a number!";
    redo GUESS;
  }

  if ($guess == $number) {
    say "Congrats, you were on target!";
    last GUESS;
  } elsif ($guess < $number) {
    say "Nay, your guess was TOO SMALL.";
  } elsif ($guess > $number) {
    say "Nay, your guess was TOO BIG.";
  } else {
    die "Illegal state";
  }

  if ($guess_no == $max_guesses) {
    say "However, you have wasted all your guesses. YOU LOOSE.";
    last GUESS;
  }
}

示例用法:

$ perl guess-the-number.pl
(1) Please enter a guess:
15
Nay, your guess was TOO SMALL.
(2) Please enter a guess:
60
Nay, your guess was TOO BIG.
(3) Please enter a guess:
45
Nay, your guess was TOO BIG.
(4) Please enter a guess:
30
Nay, your guess was TOO SMALL.
(5) Please enter a guess:
38
Congrats, you were on target!

(所有其他极端情况(猜测太多,非数字作为输入)按预期工作)

我做了什么不同的事情?

  • 当猜测太小时我没有循环(←错误!)。相反,我对每个猜测进行了循环迭代。但是,while (1)循环也可以。
  • 我使用简单的正则表达式对输入进行了完整性检查。它断言输入将被 Perl 视为数字。否则,您可以重做猜测。
  • 我一声明它们就初始化所有变量。这消除了在错误消息中弹出未初始化值的任何可能性。
  • 我使用适当的比较运算符。Perl 标量有两种形式:字符串型和数字型:

    Stringy  Numeric
    lt       <
    le       <=
    eq       ==
    ne       !=
    ge       >=
    gt       >
    cmp      <=>
    
  • say函数打印字符串 like print,但附加一个换行符。\n这将删除字符串开头或结尾的akward 。它使阅读代码更容易。
于 2012-11-28T02:24:20.603 回答
2

你没有提到哪一行是第 25 行!这可能很重要。

不过,看看你的while说法。请注意,您正在与 进行比较$guess$target但尚未设置$guess。这是你的while循环中设置的。这就是你得到未定义变量的原因。

我也没有看到在哪里$counter设置。这也可能是一个问题。

让我们仔细看看你的while循环:

while ( $guess < $target ) {
    blah, blah, blah
}

假设有一个$guess,您将循环直到用户猜出一个大于 的数字$target。那是你要的吗?不,你说你想给用户八回合,就是这样。你想要的是更多类似的东西:

 my $count = 1;
 while ( $count <= 8 ) {
     $count++;
     blah, blah, blah
 }

一个更好的方法是使用 for 循环:

for ( my $count = 1; $count <= 8; $count++ ) {
    blah, blah, blah
}

然而,这些 C 风格的 for 循环是如此过时。1..8另外,使用语法有一种更清晰的方法。这和说的一样(1, 2, 3, 4, 5, 6, 7, 8)。它干净且易于理解:

 for my $counter (1..8) {
     blah, blah, blah
 }

注意:有些人在执行这种类型的循环时使用foreach关键字而不是for关键字,以将其与 C 样式的 for 循环区分开来。但是,有些人(咳!达米安康威,咳!)不赞成使用,foreach因为它没有并没有真正增加清晰度。)

让我们看看你的逻辑的另一个方面:

if ($guess eq $target) {
    print "\nCongratulations! You guessed the secret number $target in   $counter";
}
elsif ($guess > $target) {
    print "\nYour guess, $guess, is too high.";
}
elsif ($guess < $target) {
    print "\nYour guess, $guess, is too low.";
}
else {  #Do this if the guess isn't equal to, greater than or less than the target
    print "You lose. The number was $target.";
}

你的else条款什么时候执行?答案是永远不会。毕竟,除非$guess既不大于、等于或小于,您也不会执行它$target。此外,即使您确实失败了,您仍然处于该循环中。

您可能想要做的是将You lost线放在循环之外。这样,在第八次猜测并且你的循环结束后,你会得到You lost spiel。

另外,让我们看看这一行:

chomp ( $guess = <> );
print "Enter guess $counter: ";

首先,<>null 文件句柄运算符,它与以下非常非常不同<STDIN>

空文件句柄 <> 很特殊:它可以用来模拟 sed 和 awk 的行为,以及任何其他接受文件名列表的 Unix 过滤程序,对来自所有文件名的每一行输入执行相同的操作。<> 的输入要么来自标准输入,要么来自命令行中列出的每个文件。它是这样工作的:第一次评估 <> 时,检查 @ARGV 数组,如果它为空,则 $ARGV[0] 设置为“-”,打开时会为您提供标准输入。然后将@ARGV 数组作为文件名列表进行处理。

你想要的是<STDIN>.

另请注意,在打印提示之前您正在获取输入。你真的想这样做:

print "Enter guess $counter: ";
chomp ($guess = <STDIN>);

此外,您希望设置$|为非零值以关闭缓冲。否则,您可能仍需要$guess在看到提示之前输入您的。

这是修改后的程序Modern::Perl在我的系统上不起作用,所以我使用三个单独的 pragma 来覆盖它:

#! /usr/bin/env perl
use strict;
use warnings;
use feature qw(say);

use constant {
    UPPER_RANGE => 100,
    MAX_GUESSES => 8,
};

$| = 1;
my $target = int ( ( rand UPPER_RANGE ) + 1 );

for my $counter (1..MAX_GUESSES) {
    print "Enter guess #$counter: ";
    chomp ( my $guess = <STDIN> );

    if ($guess == $target) {
        say "Congratulations! You guessed the secret number $target in $counter turns";
        exit;
    }   
    elsif ($guess > $target) {
        say "Your guess, $guess, is too high.";
    }   
    else {
        say "Your guess, $guess, is too low.";
    }   
}   
say "You lose. The number was $target.";

有几件事我没有涉及:

  • 进行数字比较时,使用==运算符而不是eq字符串。
  • 我尽可能使用say而不是使用print。该say命令就像print它自动为我放在最后的 NL。
  • 注意exit;我说你赢了之后。我想在那时结束我的程序。
  • 请注意,在我需要它们之前,我不会声明所有变量。在我的程序中,$guess$counter存在于for循环内部,而$target存在于循环内部和外部for
  • use constant帮助避免神秘数字。例如,1..MAX_GUESSES帮助我理解循环会达到我的最大猜测,而1..8不能解释为什么我会去 8。另外,我可以简单地改变我的常数,突然你有 10 次改变猜测 1 到 1000 之间的数字.
于 2012-11-28T05:13:15.660 回答
1

这里的问题是您没有显式初始化变量。默认情况下,Perl 将您创建的变量初始化my()为该undef值。因此,请查看脚本中的以下行:

my ($guess,$target,$counter);

这一行创建了三个变量,所有变量都设置为undef。有关my()运算符的详细信息,请查看perlsub 文档。这是该文档页面的相关引用:

如果需要,可以分配 my() 的参数列表,这允许您初始化变量。(如果没有为特定变量提供初始化程序,则使用未定义的值创建它。)

于 2012-11-28T02:23:11.863 回答