我想编写一个perl程序来生成具有长度为8的非重复单位数字(随机顺序的数字1-8)和第九个元素作为下划线的数组。我写了这样的代码。我想将这个生成的数组用于基于数字的益智游戏。
@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))。为什么要吃我的记忆?或者是否有任何有效的方法来编写相同的程序?
答案 0 :(得分: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;
答案 1 :(得分: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 _
答案 2 :(得分:4)
当然,使用shuffle
是解决问题的正确方法。这是关于你的代码出了什么问题的讨论,以及如何更优雅地表达这样的算法。
一旦你use warnings
,就会收到关于深度递归的警告 - 调用堆栈正在占用你的记忆。在for循环中,在第8个元素(索引7)填充后调用randgen
。在这种情况下,递归条件总是正确的(因为八个元素是唯一的,现在总是存在$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
中的值等于$rand
,它就会返回true。