Вы должны разбить подпрограмму: вычисление расстояний и построение гистограммы - это две разные вещи, и при объединении этих двух значений теряется большая ясность.
Сначала начните с самого простого решения. Я понимаю потенциальную оптимизацию с использованием отсортированного @long
, но прибегаю к этому, только если List :: Util :: min медленный.
Вы можете использовать Статистика :: Описательный для генерации распределения частоты.
#!/usr/bin/perl
use strict; use warnings;
use List::Util qw( min );
use Statistics::Descriptive;
my $stat = Statistics::Descriptive::Full->new;
my @long = (100, 200, 210, 300, 350, 400, 401, 402, 403, 404, 405, 406);
my @short = (3, 6, 120, 190, 208, 210, 300, 350);
for my $x ( @short ) {
$stat->add_data(find_dist($x, \@long));
}
my $freq = $stat->frequency_distribution_ref([0, 2, 10, 20, 94, 97]);
for my $bin ( sort { $a <=> $b } keys %$freq ) {
print "$bin:\t$freq->{$bin}\n";
}
sub find_dist {
my ($x, $v) = @_;
return min map abs($x - $_), @$v;
}
Выход:
[sinan@archardy so]$ ./t.pl
0: 3
2: 1
10: 1
20: 1
94: 1
97: 1
Конечно, это можно сделать без использования каких-либо модулей и использования вашего предположения о отсортированном @long
:
#!/usr/bin/perl
use strict; use warnings;
my @long = (100, 200, 210, 300, 350, 400, 401, 402, 403, 404, 405, 406);
my @short = (3, 6, 120, 190, 208, 210, 300, 350);
my @bins = reverse (0, 2, 10, 20, 94, 97);
my %hist;
for my $x ( @short ) {
add_hist(\%hist, \@bins, find_dist($x, \@long));
}
for my $bucket ( sort { $a <=> $b } keys %hist ) {
print "$bucket:\t$hist{$bucket}\n";
}
sub find_dist {
my ($x, $v) = @_;
my $min = abs($x - $v->[0]);
for my $i ( 1 .. $#$v ) {
my $dist = abs($x - $v->[$i]);
last if $dist >= $min;
$min = $dist;
}
return $min;
}
sub add_hist {
my ($hist, $bins, $x) = @_;
for my $u ( @$bins ) {
if ( $x >= $u ) {
$hist{ $u } += 1;
last;
}
}
return;
}