3

私はPerlに非常に慣れていないので、ユーザーに1から100までの数字を推測する8つのチャンスが与えられる単純な推測ゲームである割り当てが与えられました。上記のエラーが発生し続け、理解できません。

これが私のコードです:

    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スカラーには、文字列と数値の2つの種類があります。

    Stringy  Numeric
    lt       <
    le       <=
    eq       ==
    ne       !=
    ge       >=
    gt       >
    cmp      <=>
    
  • このsay関数は、のようprintに文字列を出力しますが、改行を追加します。\nこれにより、文字列の最初または最後にある厄介なsが削除されます。コードの読み取りが容易になります。
于 2012-11-28T02:24:20.603 に答える
2

どの行が25行目であるかについては言及していません。それは重要かもしれません。

しかし、あなたのwhile声明を見てください。と比較$guessして$targetいるが、まだ設定していないことに注意してください$guess。それはあなたのループの中に設定されています。whileこれが、未定義の変数を取得する理由です。

どこに設定されているかもわかりません$counter。それも問題になる可能性があります。

whileループをもう少し詳しく見てみましょう。

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

が存在すると仮定すると$guess、ユーザーが。よりも大きい数を推測するまでループします$target。それはあなたが望むものですか?いいえ、ユーザーに8ターンを与えたいとおっしゃいましたが、それだけです。あなたが欲しいのは、次のようなものです。

 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に、キーワードの代わりにキーワードを使用して、Cスタイルのforループと区別する人もいます。ただし、一部の人(咳!ダミアンコンウェイ、咳!)は、使用しないため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。また、あなたが負けたとしても、あなたはまだそのループにいます。

あなたがおそらくやりたいことは、あなたが負けたラインをループの外に置くことです。そうすれば、8番目の推測とループが終了した後、あなたはあなたがスピルを失うことになります。

また、この行を見てみましょう:

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

まず、これ<>nullファイルハンドル演算子であり、次のものとは大きく異なり<STDIN>ます。

nullファイルハンドル<>は特別です。これは、sedとawk、およびファイル名のリストを取得する他のUnixフィルタープログラムの動作をエミュレートするために使用でき、それらすべてからの入力の各行に対して同じことを行います。<>からの入力は、標準入力、またはコマンドラインにリストされている各ファイルからの入力です。仕組みは次のとおりです。最初に<>が評価されると、@ ARGV配列がチェックされ、空の場合は$ ARGV [0]が「-」に設定されます。これを開くと、標準の入力が得られます。@ARGV配列は、ファイル名のリストとして処理されます。

あなたが欲しいのはです<STDIN>

また、プロンプトを印刷する前に入力を取得していることに注意してください。あなたは本当に代わりにこれをしたいです:

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

また、$|バッファリングをオフにするには、ゼロ以外の値に設定する必要があります。それ以外の場合$guessは、プロンプトが表示される前に入力する必要があります。

Modern::Perlこれが私のシステムでは動作しない改訂されたプログラムです、それで私はそれをカバーするために3つの別々のプラグマを使用します:

#! /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$targetfor
  • 不思議な数字use constantを避けるのを手伝います。たとえば、ループが最大の推測になっていることを理解するのに役立ちますが、なぜ8に行くのかは説明されていません。また、定数を変更するだけで、突然、1から1000までの数値を10回変更できます。 。1..MAX_GUESSES1..8
于 2012-11-28T05:13:15.660 に答える
1

ここでの問題は、変数を明示的に初期化していないことです。デフォルトでは、Perlは作成した変数を値に初期化しmy()ますundef。したがって、スクリプトの次の行を見てください。

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

この行は3つの変数を作成し、それらはすべてに設定されていundefます。my()オペレーターの詳細については、 perlsubのドキュメントをご覧ください。そのドキュメントページからの関連する引用は次のとおりです。

my()のパラメータリストは、必要に応じて割り当てることができます。これにより、変数を初期化できます。(特定の変数に初期化子が指定されていない場合は、未定義の値で作成されます。)

于 2012-11-28T02:23:11.863 に答える