[英]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.