-1

長さ8の非反復的な1桁の数字(ランダムな順序で1〜8の数字)と9番目の要素がアンダースコアになる配列を生成するperlプログラムを作成したかったのです。このようなコードを書きました。この生成された配列を数値ベースのパズル ゲームに使用したいと考えています。

@mat = (0,0,0,0,0,0,0,0,0);

sub randgen { 

    $randigit = int(rand(9));

    if ($randigit == 0) {  
        &randgen;   
    }

    elsif (    $mat[0] == $randigit
            || $mat[1] == $randigit
            || $mat[2] == $randigit
            || $mat[3] == $randigit
            || $mat[4] == $randigit
            || $mat[5] == $randigit
            || $mat[6] == $randigit
            || $mat[7] == $randigit
            || $mat[8] == $randigit
          )
    {
        &randgen;
    }
}

&randgen;

for ( $assign = 0; $assign <= 8; $assign++) {

    $mat[$assign] = $randigit;
    print "@mat \n"; # To see to what extent the program has generated the array
    &randgen;
}

for ($i = 0; $i <= $#mat; $i++) {   

    $sum = $sum + $mat[$i];
}

$miss = 36 - $sum ;
$mat[7] = $miss;
$mat[8] = "_";
print "@mat \n";

プログラム 7 番目の要素を割り当てた後、私のプログラムはメモリ (10 GB) を消費し始めました。その理由がわかりません。数学的論理を使用して、欠落している数値を見つけました (数値の合計 - 36 (n(n+1)/2))。なぜ私の記憶を食べているのですか?または、同じプログラムを作成する効果的な方法はありますか?

4

3 に答える 3

6

同じプログラムを書く効果的な方法はありますか?

あなたは賭けます。必要なのは数行だけです。

use List::Util 'shuffle';       # import shuffle
my @array = shuffle( 1 .. 8 );  # shuffle 1 to 8
push @array, '_';               # add the underscore

一行で:

my @array = ( shuffle( 1 .. 8 ), '_' );

次の点を考慮してください。

  • サブをreturn値に構造化する
  • 同じことを&randgen;するときは書くのを避けるrandgen();
  • レキシカルスコープmy
  • use strict; use warnings;
  • 可能な限り再帰呼び出しを行わないようにします (これがメモリ消費の原因となる可能性があります)。
于 2013-01-08T14:43:02.620 に答える
5

私はあなたの記憶を食べているものを見ていませんが、あなたが望むものを達成するためのより簡単なアプローチは次のとおりです:

#!/usr/bin/env perl
use strict;
use warnings;
use List::Util 'shuffle';

my @mats = shuffle 1 .. 8;
push @mats, '_';

print "@mats\n";

出力例:

3 1 5 8 2 7 4 6 _
于 2013-01-08T14:42:59.970 に答える
4

もちろん、使用shuffleはあなたの問題に対する唯一の正しい方法です。これは、あなたのコードで何がうまくいかなかったのか、そしてそのようなアルゴリズムをよりエレガントに表現するにはどうすればよいかについての議論です。

するとuse warnings、深層再帰に関する警告が表示されます — コール スタックがメモリを消費しています。for ループではrandgen、8 番目の要素 (インデックス 7) が満たされた後に呼び出します。その場合、再帰条件は常に真です (8 つの要素が一意であるため、$randigit等しい要素が常に存在します)。

コードには他にも改善できる点がいくつかあります。

次のようなループfor ($i = MIN; $i <= MAX; $i++)は、foreach ループとして記述した方が適切です。

for my $i (MIN .. MAX)

サブルーチンは、パラメーター (これらは にあります@_) を取り、値を返すことができます。これにより、引数を伝えるためにグローバル変数を使用する必要がなくなります。

また、use strict; use warnings. これは多くのエラーを指摘し、すべての変数を宣言することを強制します (変数は で宣言できますmy)。

これがあなたのコードの更新版です。再帰機能はありませんが、ループです。そして、それは完璧に動作します:

#!/usr/bin/perl

use strict; use warnings;

# randgen takes
#   - the largest number that may be used, and
#   - a list of all forbidden numbers.
sub randgen { 
    my ($max, @forbidden) = @_; # unpack arguments
    my $rand;
    do {
        $rand = int rand($max + 1);
    } while grep {$_ == $rand} 0, @forbidden;
    return $rand;  # return the rand value.
}

my $digits = 8;
my @mat;  # don't prefill fields.
for ( 1 .. $digits ) {                 # do $digits times 
    push @mat, randgen($digits, @mat); # push appends item to array
    print "@mat\n";
}
push @mat, "_";

print "@mat\n";

出力例:

8
8 4
8 4 7
8 4 7 2
8 4 7 2 3
8 4 7 2 3 5
8 4 7 2 3 5 1
8 4 7 2 3 5 1 6
8 4 7 2 3 5 1 6 _

grepビルトインは、式またはブロックと、値のリストを取ります。式が true と評価されるすべての値を返します。現在のアイテムには からアクセスできます$_@forbidden私のループでは、 に値または0等しい値がある限り true を返します$rand

于 2013-01-08T15:21:21.760 に答える