Есть ли более сжатый способ написать это на Perl? - PullRequest
4 голосов
/ 29 октября 2010

Эта подпрограмма передается массиву файловых дескрипторов для закрытия, которые она закрывает один за другим с помощью цикла foreach:

sub closef
{
    foreach(@_) {
        my $fh = shift;
        close $fh;
    }   
}

Где все это простая подпрограмма может быть изменена, чтобы сделать ее лучше?

Стоит ли использовать close shift вместо того, чтобы делать это в две строки?

Ответы [ 6 ]

12 голосов
/ 29 октября 2010

Самый краткий способ - использовать лексические дескрипторы файлов, которые автоматически закрываются при выходе из области видимости:

sub firstline {
    open( my $in, shift ) && return scalar <$in>;
    # no close() required
}

См. Perldoc Перлопентут .

8 голосов
/ 29 октября 2010
sub closef { close $_ for @_ }

Но у функции close есть возвращаемое значение, и вам, как правило, было бы неплохо проверить его. Таким образом,

sub closef { map { close $_ } @_ }
7 голосов
/ 29 октября 2010
foreach(@_) {
    close $_;
}   

или

foreach my $fh (@_) {
    close $fh;
}   

или

close $_ foreach (@_);
4 голосов
/ 29 октября 2010

Существует проблема с первоначально опубликованной подпрограммой.

Функция shift ожидает массив в качестве аргумента.Неявная переменная внутри foreach является скаляром.

Это означает, что $fh остается неинициализированным.Причина, по которой это, вероятно, остается незамеченной, заключается в том, что (, как указывает Евгений y ), лексические файловые дескрипторы закрываются, когда выходят за рамки.

Кстати, use warnings; будет уведомлять о таких вещах.

Более идиоматическим подходом было бы использование цикла while:

sub closef {

    while ( my $fh = shift @_ ) {
        close $fh;
    }
}

Для более компактной записи цикл for - это путь:

sub closef { close for @_; }

Эквивалент с циклом while не так читабелен:

sub closef { close while $_ = shift }

Обновление

Комментарий Майкла Кармана, приведенный ниже, остается в силе.shift изменяет перебираемый массив, что делает его небезопасной операцией.На самом деле это проблема .

4 голосов
/ 29 октября 2010

Ваш код не делает то, что вы думаете.Вы говорите, что хотите закрыть каждый файловый дескриптор, переданный подпрограмме, но закрываете только некоторые из них.Рассмотрим следующий пример:

sub t{
        foreach(@_){
                my $tmp = shift;
                print $tmp;
        }
}

t(('a'...'d'));

Это выдаст

ab

Несмотря на то, что списком аргументов являются a, b, c и d.Это потому, что на каждой итерации вызывается сдвиг.Чтобы получить желаемое поведение, используйте

sub t{
        foreach(@_){
                my $tmp = $_;
                print $tmp;
        }
}

t(('a'...'d'));

Или, лучше, не создавайте копию $ _, просто назовите ее:

sub t{
        foreach my $tmp (@_){
                print $tmp;
        }
}

t(('a'...'d'));
2 голосов
/ 29 октября 2010

Или вы также можете использовать карту:

map { close $_ } @_;

С картой вы даже можете получить массив всех результатов близких вызовов, если хотите обработать его.

my @results = map { close $_ } @_;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...