簡體   English   中英

用於多個范圍的循環的Perl

[英]Perl for loop for multiple ranges

在for循環中設置范圍計數器的最佳方法是什么? 我有一個制表符分隔輸入文件,其中前兩列很重要。 我想找到它們在Pos值范圍內出現的分數的最小值和最大值。 所以對於示例輸入文件:

Pos     Score
1       5
2       17
9       80
38      22
40      11
7       0
302     19
85      33
12      51
293     1
5       19
61      8
71      15

如果存在,我需要計算每個范圍的最小和最大分數。

1-29 (min=?, max=?)
30-59 (min=?, max=?)
60-89 (min=?, max=?)

預期成績:

1-29 (min=0, max=80)
30-59 (min=11, max=22)
60-89 (min=8, max=33)
290-219 (min=1, max=19)

還有另一個與此相關的線程,但它們只計算具有設定范圍的事件。 我的嘗試是設置for循環:

use List::MoreUtils qw( minmax );
my %inputhash;
my %storehash;

open (FF,$inputfile) || die "Cannot open file $inputfile";
while(<FF>) {
    next if $. < 2; #use to trim off first line if there is a header
    my ($Pos, $Score)  = split;
    $inputhash{$Pos} = $Score;
}


for (my $x=1; $x<1600; $x+29) #set to 1600 for now
{
    my $low = $x;
    my $high = $x+29;
    foreach my $i ($low...$high)
    {
        if (exists $inputhash{$i})
        {
            my $score = $inputhash{$i};
            push (@{$storehash{$high}}, $score);
        }
    }
} 

foreach my $range (sort {$a <=> $b} keys %storehash)
{
    my ($minrange, $maxrange) = minmax @{$storehash{$range}};
    print "$range: $minrange, $maxrange\n";
}

有沒有更好的方法來處理這個? 這個當前的實現給了我一個錯誤:在void上下文中無用的添加(+)。

如果將數據推送到數組中,而不是哈希:

$inputarray[$Pos] = $Score;

您可以在數組切片上使用minmax (在刪除任何未定義的值之后):

my ($min, $max) = minmax grep {defined} @inputarray[0..3];

例如

#!/usr/bin/perl
use strict;
use warnings;

use List::MoreUtils qw(minmax);
use List::Util qw(min);

my @inputarray;
<DATA>;
while (<DATA>) {
    my ($pos, $score) = split;
    $inputarray[$pos] = $score;
}

for (my $i = 1; $i < @inputarray; $i += 29) {
    my $end = min($i + 29, $#inputarray); # Don't overrun the end of the array.
    my ($min, $max) = minmax grep {defined} @inputarray[$i..$end];
    print "$i-$end (min=$min,max=$max)\n" if defined $min;
}

__DATA__
Pos     Score
1       5
2       17
9       80
38      22
40      11
7       0
302     19
85      33
12      51
293     1
5       19
61      8
71      15

輸出:

1-30 (min=0,max=80)
30-59 (min=11,max=22)
59-88 (min=8,max=33)
291-302 (min=1,max=19)
use strict;
use warnings;

use List::Util qw(max min);

my $step = 30;  # group into 30 item ...
my @bins;       # ... bins

<DATA>;         # skip line
while (<DATA>) {
  my ($p, $s) = split;
  push @{$bins[$p / $step]}, $s; 
}

for (my $i = 0; $i < @bins; $i++) {
    next if not $bins[$i];
    printf("%d, %d  (min %d, max %d)\n", 
        $i * $step, ($i + 1) * $step, 
        min(@{$bins[$i]}), max(@{$bins[$i]}));
}

__DATA__
Pos     Score
1       5
2       17
9       80
38      22
40      11
7       0
302     19
85      33
12      51
293     1
5       19
61      8
71      15

產量

0, 30  (min 0, max 80)
30, 60  (min 11, max 22)
60, 90  (min 8, max 33)
270, 300  (min 1, max 1)
300, 330  (min 19, max 19)

使用命令行,

perl -ane'
  /\d/ or next;
  $i = int($F[0] /30);
  (!defined or $_ >$F[1]) and $_ = $F[1] for $r[$i]{m};
  (!defined or $_ <$F[1]) and $_ = $F[1] for $r[$i]{M};
  }{
  printf("%d-%d (min=%d, max=%d)\n", $_*30, $_*30+29, $r[$_]{m}, $r[$_]{M})
    for grep $r[$_], 0 .. $#r;
' file

產量

0-29 (min=0, max=80)
30-59 (min=11, max=22)
60-89 (min=8, max=33)
270-299 (min=1, max=1)
300-329 (min=19, max=19)

腳本相當於命令行版本,

my @r;
while (<>) {
  /\d/ or next;
  my @F = split;
  my $i = int($F[0] /30);
  # min topicalizer, refer to $r[$i]{m} as $_
  for ($r[$i]{m}) {
    $_ = $F[1] if !defined or $_ >$F[1];
  }
  # max topicalizer
  for ($r[$i]{M}) {
    $_ = $F[1] if !defined or $_ <$F[1];
  }
}

for (grep $r[$_], 0 .. $#r) {
  printf("%d-%d (min=%d, max=%d)\n", $_*30, $_*30+29, $r[$_]{m}, $r[$_]{M});
}

錯誤消息

Useless use of addition (+) in void context

應該已經提醒你你的for循環的最后一個子句是$x+29而不是$x += 29 除此之外,您在范圍上有簡單的邊界錯誤

如果您的范圍寬度都是相同的大小,那么最簡單的方法是通過簡單划分計算每個位置的范圍,並為每個范圍構建一個得分列表。 之后可以確定每個范圍中的最小值和最大值

該解決方案使用恆定的WIDTH來確定每個范圍的大小; 在這種情況下它是30

use strict;
use warnings;
use autodie;

use List::MoreUtils 'minmax';
use constant WIDTH => 30;

<>; # lose the header

my @buckets;
while (<>) {
  my ($pos, $score) = split;
  push @{ $buckets[$pos / WIDTH] }, $score;
}

for my $i (0 .. $#buckets) {
  next unless my $contents = $buckets[$i];
  my $start = $i * WIDTH;
  printf "%d-%d (min=%d, max=%d)\n",
      $start, $start + WIDTH - 1,
      minmax @$contents;
}

產量

0-29 (min=0, max=80)
30-59 (min=11, max=22)
60-89 (min=8, max=33)
270-299 (min=1, max=1)
300-329 (min=19, max=19)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM