在Perl中的数字lt(<)第25行中使用未初始化的值$ guess

时间:2012-11-28 01:42:08

标签: perl random

我是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.";
        }
    }

3 个答案:

答案 0 :(得分: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:

    Stringy  Numeric
    lt       <
    le       <=
    eq       ==
    ne       !=
    ge       >=
    gt       >
    cmp      <=>
    
  • say函数打印字符串print,但附加换行符。这将删除字符串开头或结尾的akward \n。它使阅读代码更容易。

答案 1 :(得分: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-Style for循环。有些人(咳嗽!Damian Conway,咳嗽!)对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 lose 行放在循环之外。这样,在第八次猜测和你的循环结束后,你得到你输了 spiel。

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

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

首先,<> null文件句柄操作符,它与<STDIN>非常不同:

  

null文件句柄&lt;&gt;很特别:它可以用来模拟sed和awk的行为,以及任何其他带有文件名列表的Unix过滤器程序,对所有输入的每一行做同样的事情。来自&lt;&gt;的输入来自标准输入,或来自命令行中列出的每个文件。以下是它的工作原理:第一次&lt;&gt;如果进行评估,则检查@ARGV数组,如果为空,则将$ ARGV [0]设置为“ - ”,打开时会为您提供标准输入。然后将@ARGV数组作为文件名列表处理。

你想要的是<STDIN>

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

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

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

以下修改后的程序Modern::Perl对我的系统不起作用,因此我使用三个单独的编译指示来覆盖它:

#! /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代替printsay命令与print类似,不过它会自动为我添加一个NL。
  • 在我说你赢了之后注意exit;。我想在那时结束我的计划。
  • 请注意,在我需要之前,我不会声明所有变量。在我的计划中,$guess$counter仅存在于for循环内,而$target循环内外均存在for
  • use constant以帮助避免神秘的号码。例如,1..MAX_GUESSES帮助我理解循环是我的最大猜测,而1..8无法解释为什么我要去8。而且,我可以简单地改变我的常数,突然间你有猜测数字在1到1000之间的10次更改。

答案 2 :(得分:1)

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

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

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

  

如果需要,可以将my()的参数列表分配给,允许   你初始化你的变量。 (如果没有给出初始化器   特殊变量,使用未定义的值创建。)