perl - 这个 Perl 代码如何从数组中选择两个不同的元素?

标签 perl

我从一个人那里继承了一些代码,他过去最喜欢将每一行缩短到绝对最小值(有时只是为了让它看起来很酷)。他的代码很难理解,但我设法理解(并重写)了其中的大部分。

现在我偶然发现了一段代码,无论我怎么努力,我都无法理解。

my @heads = grep {s/\.txt$//} OSA::Fast::IO::Ls->ls($SysKey,'fo','osr/tiparlo',qr{^\d+\.txt$}) || ();
my @selected_heads = ();
for my $i (0..1) {
   $selected_heads[$i] = int rand scalar @heads;
   for my $j (0..@heads-1) {
      last if (!grep $j eq $_, @selected_heads[0..$i-1]);
      $selected_heads[$i] = ($selected_heads[$i] + 1) % @heads; #WTF?
   }
   my $head_nr = sprintf "%04d", $i;
   OSA::Fast::IO::Cp->cp($SysKey,'',"osr/tiparlo/$heads[$selected_heads[$i]].txt","$recdir/heads/$head_nr.txt");
   OSA::Fast::IO::Cp->cp($SysKey,'',"osr/tiparlo/$heads[$selected_heads[$i]].cache","$recdir/heads/$head_nr.cache");
}

据我所知,这应该是某种随机化器,但我从未见过更复杂的实现随机性的方法。还是我的假设是错误的?至少,这就是这段代码应该做的。选择 2 个随机文件并复制它们。

=== 注释 ===

OSA 框架是我们自己的框架。它们以它们的 UNIX 对应物命名,并进行一些基本测试,以便应用程序无需为此烦恼。

最佳答案

这看起来像一些带有 Perl 语法的 C 代码。有时,了解这个人所用的语言可以帮助您弄清楚发生了什么。在这种情况下,这个人的大脑被内存管理、指针运算和其他低级关注的内部运作所感染,所以他想精细地控制一切:

my @selected_heads = ();

# a tricky way to make a two element array
for my $i (0..1) {

   # choose a random file
   $selected_heads[$i] = int rand @heads;

   # for all the files (could use $#heads instead)
   for my $j (0..@heads-1) {
      # stop if the chosen file is not already in @selected_heads
      # it's that damned ! in front of the grep that's mind-warping
      last if (!grep $j eq $_, @selected_heads[0..$i-1]);

      # if we are this far, the two files we selected are the same
      # choose a different file if we're this far
      $selected_heads[$i] = ($selected_heads[$i] + 1) % @heads; #WTF?
   }

...
}

这是很多工作,因为原始程序员要么不理解散列,要么不喜欢它们。
my %selected_heads;
until( keys %selected_heads == 2 )
    {
    my $try = int rand @heads;
    redo if exists $selected_heads{$try};
    $selected_heads{$try}++;
    }

my @selected_heads = keys %selected_heads;

如果您仍然讨厌散列并且拥有 Perl 5.10 或更高版本,则可以使用智能匹配来检查值是否在数组中:
my @selected_heads;
until( @selected_heads == 2 )
    {
    my $try = int rand @heads;
    redo if $try ~~ @selected_heads;
    push @selected_heads, $try;
    }

但是,您对这个问题有一个特殊的限制。由于您知道只有两个元素,因此您只需检查要添加的元素是否是前一个元素。在第一种情况下它不会是 undef,所以第一个添加总是有效的。在第二种情况下,它不能是数组中的最后一个元素:
my @selected_heads;
until( @selected_heads == 2 )
    {
    my $try = int rand @heads;
    redo if $try eq $selected_heads[-1];
    push @selected_heads, $try;
    }

呵呵。我不记得上次使用 until 是什么时候了当它真正适合问题时。 :)

请注意,所有这些解决方案都有一个问题,如果原始文件的数量小于 2,它们可能会导致无限循环。我会在更高的位置添加一个保护条件,以便通过错误处理无文件和单个文件的情况,也许是两个文件盒不会费心订购它们。

另一种方法是混洗(例如,使用 List::Util )整个原始文件列表,然后去掉前两个文件:
use List::Util qw(shuffle);

my @input = 'a' .. 'z';

my @two = ( shuffle( @input ) )[0,1];

print "selected: @two\n";

关于perl - 这个 Perl 代码如何从数组中选择两个不同的元素?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2699623/

相关文章:

regex - Perl 正则表达式捕获行首和行尾之间的字符串(行尾有一个带字符的空格)

perl - 有没有办法在使用严格时从 Perl 中的字符串/变量创建变量?

perl - 无法使用 AnyEvent::Socket 和 tcp_connect(到 UNIX 域套接字)读取客户端消息

mysql - Catalyst 创建架构无法连接,但我可以

perl - 为什么在我尝试安装 GD::Polygon 时得到 "Could not find gdlib-config in the search path",即使安装了 gdlib-config?

perl - 如何检查 Perl 中是否已创建对象?

forms - perl WWW::Mechanize Submit_form() 直接写入文件?

Perl:分配 [] 或 {} 是否昂贵?如何快速重置数字/关联数组?

java - MATLAB - 删除二进制文件的元素而不加载整个文件

mysql - 使用 Perl 插入 MySQL