Для подсчета и управления диапазоном элементов, а также для любой структурированной информации очень полезны хэши. С их помощью и массивами вы можете с помощью ссылок создавать структуры данных, которые могут кодировать довольно сложные отношения. См. perldsc . Как только они станут громоздкими, следующий шаг - написать класс.
Он может идти по этим направлениям
use warnings;
use strict;
use feature 'say';
use Data::Dump qw(dd);
my $file = shift @ARGV;
die "Usage: $0 filename\n" if not $file;
open my $fh, '<', $file or die "Can't open $file: $!";
my %results;
while (<$fh>) {
next if not /\S/; # skip empty lines
my ($subj, $name, $grade) = split;
if (not $subj or not $name or not defined $grade) {
warn "Incomplete data, line: $_";
next;
}
if ($grade eq 'pass') {
$results{$name}->{pass}++;
}
elsif ($grade eq 'fail') {
$results{$name}->{fail}++;
}
else {
warn "Unknown grade format for $name in $subj: $grade";
next;
}
}
dd \%results;
Имена без единого вхождения конкретного балла остаются без хеш-значения для этого балла. Если вам нужны эти записи, то постобработка %results
для их добавления, например
foreach my $name (keys %results) {
$results{$name}->{pass} = 0 if not exists $results{$name}->{pass};
$results{$name}->{fail} = 0 if not exists $results{$name}->{fail};
}
Или добавьте оператор, чтобы инициализировать обе оценки для каждой записи (имени) в коде.
Обратите внимание, что мы можем расширить эту структуру данных, чтобы хранить больше информации (например, $subj
) по мере необходимости, аккуратно и с небольшими изменениями кода. Это еще одно преимущество использования хэшей.
Несколько комментариев к опубликованному коду
Почему в начале нет use warnings;
? Вы должны иметь это; напрямую полезно
Используйте лексические дескрипторы файлов, open my $fh, '<', $file ...
вместо глобусов (FILE
)
Обрабатывать файлы по очереди за раз, если нет определенной причины прочитать все сначала
Вы всегда хотите сравнить вводимые данные с ожидаемыми; что именно это зависит от вашей проблемы и конструкции. В приведенном выше коде все поля должны быть определены и разумны (оценка допускается равной 0), в то время как вы можете фактически принять несуществующую оценку; отрегулируйте как подходит.
Конечно, если все файлы, которые эта программа когда-либо будет читать , были бы похожи на то, что вы показываете, всегда со всеми полями и только с проходной / неудачной оценкой, тогда не было бы необходимости проверять ввод
Образец /\s+/
в split почти всегда следует заменять на ' '
, то же самое, но он также отбрасывает ведущие пробелы. Это также значение по умолчанию, наряду с $_
для строки, таким образом, просто split;
выше (строка, которую она разделяет на ' '
, равна $_
)
Вы можете построить структуру данных, подобную той, которая используется здесь, с тем, что вы получили. Однако я не вижу преимущества массива здесь
Приращение соответствующего счетчика можно записать более кратко
while (<$fh>) {
next if not /\S/;
my ($subj, $name, $grade) = split;
# check input ...
if ($grade !~ /^(?:pass|fail)$/) {
warn "Unknown grade format for $name in $subj: $grade";
next;
}
$results{$name}->{$grade}++;
}
Если вы предпочитаете, чтобы ваш код спокойно принимал что-нибудь в третьем поле и сохранял его в %results
с его счетом, то снимите проверку с pass|fail
.