1

我有以下文件:

a b 5
c d 6
e f 7
g h 4
i j 3
k l 10

我想在第三列中找到哪一行显示最小值并将其从初始文件中删除。在此之后,我想再次迭代程序并再次找到哪一行呈现最小值并将相同的东西再做 2 次。

输出文件应该是

c d 6
e f 7
k l 10

我尝试编写以下代码:

#!/usr/bin/perl

use strict;
use warnings;
use feature 'say';

my $file1 = "try.dat";
open my $fg,  "<", $file1 or die "Error during $file1 opening: $!";

my @vector;

while (<$fg>) {
    push @vector, [ split ];
}

my $minimum = $vector[0][2];
my @blacklist;
for my $i (0 .. $#vector) {
    if ($minimum > $vector[$i][2]){
    $minimum = $vector[$i][2];
    push @blacklist, @vector[$i+1]; 
    }
}
#say "my minimum is $minimum";
#say "the blacklist is composed by @blacklist";

我不知道如何擦除包含的元素@blacklist(在第一种情况下应该是i j 3)以及如何迭代所有内容。

对迭代有什么帮助吗?

4

3 回答 3

2

这种事情就是Tie::File为此而生的。它允许您通过修改绑定数组来就地修改文件。

这个程序做你想做的。辅助函数minidx返回传递数组中包含最小值的元素的第一个索引。

该程序通过将文件记录的第三个字段复制到数组@field3中,并在其中找到最小值的索引来工作。然后从文件中删除该索引处的元素并@field3使用splice.

use strict;
use warnings;

use Tie::File;

tie my @file, 'Tie::File', 'file.txt' or die $!;

my @field3 = map { (split)[2] } @file;
for (1 .. 3) {
  my $i = minidx(\@field3);
  splice @file, $i, 1;
  splice @field3, $i, 1;
}

sub minidx {
  my ($arr) = @_;
  my ($i, $v);
  for (0 .. $#$arr) {
    ($i, $v) = ($_, $arr->[$_]) unless defined $v and $arr->[$_] >= $v;
  }
  return $i;
}

输出

c d 6
e f 7
k l 10
于 2013-03-14T18:25:30.343 回答
1

当您说要将@blacklist = @vector整个@vector数组添加到黑名单时。你可能想做一个push @blacklist, $vector[$i]. 这会将数组引用推入黑名单。

现在,黑名单中有一个数组 ref,因此您必须尊重它才能打印它。

say "the blacklist is composed by @{$blacklist[0]}";

编辑:用于迭代和编写:

我会跳过@blacklist数组(除非你需要它来做其他事情)并从@vector. 然后你可以写@vector一些文件。

my $num_elts_to_remove = 3;
for (my $j = 0; $j < $num_elts_to_remove; $j++) {
  my $minimum = $vector[0][2];
  my $min_idx = 0;
  for my $i (0 .. $#vector) {
      if ($minimum > $vector[$i][2]){
        $minimum = $vector[$i][2];
        $min_idx = $i;
      }
  }
  push @blacklist, $vector[$min_index];
  splice @vector, $min_idx, 1; #remove array with smallest value
}

现在将数组写入文件

open OUT, ">", $outfile or die "Error: $!";
foreach(@vector) {
    print OUT join " ", @$_;
    print OUT "\n";
}
close(OUT);

印刷:

c d 6
e f 7
k l 10
于 2013-03-14T17:18:53.147 回答
0

进一步采纳 Borodin's Tie::File 建议。我编写了一个名为Tie::Array::CSV的可爱模块,它允许您将分隔文件视为一个数组(并且因为它在下面使用 Tie::File,所以它既可以读取又可以写入)。因此,我可以使用诸如 map 和 sort(以及 Schwartzian 变换!)之类的 Perlish 操作来执行此任务:

#!/usr/bin/env perl

use strict;
use warnings;

use Tie::Array::CSV;

tie my @data, 'Tie::Array::CSV', 'data', sep_char => ' ';

# get a list of row ids sorted by last value (inc)
my $i = 0;
my @sorted = 
  map  { $_->[0] } 
  sort { $a->[1] <=> $b->[1] }
  map  { [$i++, $_->[-1]] } 
  @data;

#splice the rows by index of the lowest three (from bottom to top)
splice @data, $_, 1 for reverse sort @sorted[0..2];

请注意,最后您希望从底部删除行,这样您就不必每次都重新索引。

于 2013-03-16T03:51:14.633 回答