Подпрограмма Perl может возвратить данные, но продолжать обрабатывать?

Там какой-либо путь состоит в том, чтобы иметь подпрограмму, передают данные обратно при тихой обработке? Например (этот пример раньше просто иллюстрировал) - подпрограмма читает файл. В то время как это прочитывает файл, если некоторое условие соблюдают, то "возвратите" ту строку и продолжайте обрабатывать. Я знаю, что существуют те, которые ответят - почему Вы хотели бы сделать это? и почему не делают Вас просто...?, но я действительно хотел бы знать, возможно ли это.

6
задан Brian Tompsett - 汤莱恩 15 November 2015 в 11:28
поделиться

7 ответов

Обычный способ реализовать этот тип функциональности - с помощью функции обратного вызова:

{
    open my $log, '>', 'logfile' or die $!;
    sub log_line {print $log @_}
}

sub process_file {
    my ($filename, $callback) = @_;
    open my $file, '<', $filename or die $!;
    local $_;
    while (<$file>) {
        if (/some condition/) {
             $callback->($_)
        }
        # whatever other processing you need ....
    }
}

process_file 'myfile.txt', \&log_line;

или даже без названия обратного вызова:

process_file 'myfile.txt', sub {print STDERR @_};
7
ответ дан 8 December 2019 в 17:19
поделиться

Самый простой способ сделать это в Perl - это, вероятно, решение типа итератора. Например, здесь у нас есть подпрограмма, которая формирует закрытие над дескриптором файла:

open my $fh, '<', 'some_file.txt' or die $!;
my $iter = sub { 
    while( my $line = <$fh> ) { 
        return $line if $line =~ /foo/;
    }

    return;
}

Подпрограмма выполняет итерацию по строкам, пока не найдет строку, соответствующую шаблону / foo / , а затем возвращает это, иначе ничего не возвращает. ( undef в скалярном контексте.) Поскольку дескриптор файла $ fh определен вне области действия подпрограммы, он остается резидентным в памяти между вызовами. Самое главное, что его состояние, включая текущую позицию поиска в файле, сохраняется. Таким образом, каждый вызов подпрограммы возобновляет чтение файла с того места, где он остановился в последний раз.

Чтобы использовать итератор:

while( defined( my $next_line = $iter->() ) ) { 
    # do something with each line here
}
2
ответ дан 8 December 2019 в 17:19
поделиться

Модуль Coro выглядит так, как будто он может быть полезен для этой проблемы, хотя я понятия не имею, как он работает, и не знаю, делает то, что рекламирует.

3
ответ дан 8 December 2019 в 17:19
поделиться

Если ваш язык поддерживает замыкания, вы можете сделать что-то вроде этого:

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

(Это псевдокод, похожий на javascript)

function fileReader (filename) {
    var  file = open(filename);

    return function () {
        while (s = file.read()) {
            if (condition) {
                return line;
            }
        }
        return null;
   }     
}

a = fileReader("myfile");
line1 = a();
line2 = a();
line3 = a();
-1
ответ дан 8 December 2019 в 17:19
поделиться

А как насчет рекурсивной подпрограммы? Повторное open существующие дескрипторы файлов не сбрасывают номер строки ввода, поэтому он продолжается с того места, где был остановлен.

Вот пример, в котором подпрограмма process_file выводит разделенные пустой строкой "\ n \ n" абзацы, содержащие foo.

sub process_file {

    my ($fileHandle) = @_;
    my $paragraph;

    while ( defined(my $line = <$fileHandle>) and not eof(<$fileHandle>) ) {

        $paragraph .= $line;
        last unless length($line);
    }

    print $paragraph if $paragraph =~ /foo/;
    goto &process_file unless eof($fileHandle);  
       # goto optimizes the tail recursion and prevents a stack overflow
       # redo unless eof($fileHandle); would also work
}

open my $fileHandle, '<', 'file.txt';
process_file($fileHandle);
-1
ответ дан 8 December 2019 в 17:19
поделиться

Некоторые языки предлагают такую возможность с помощью "генераторов" или "корутинов", но Perl этого не делает. На странице генераторов, ссылка на которую приведена выше, есть примеры на Python, C# и Ruby (среди прочих).

4
ответ дан 8 December 2019 в 17:19
поделиться

Если вы действительно хотите сделать это, вы можете использовать потоки. Один из вариантов - создать отдельный поток, который читает файл, и, когда он найдет определенную строку, поместить ее в массив, совместно используемый потоками. Затем другой поток может взять строки по мере их нахождения и обработать их. Вот пример, который читает файл, ищет «X» в строке файла и выполняет действие, когда он найден.

use strict;
use threads;
use threads::shared;

my @ary : shared;

my $thr = threads->create('file_reader');

while(1){
    my ($value);
    {
        lock(@ary);
        if ($#ary > -1){
            $value = shift(@ary);
            print "Found a line to process:  $value\n";
        }
        else{
            print "no more lines to process...\n";
        }            
    }

    sleep(1);
    #process $value
}


sub file_reader{

            #File input
    open(INPUT, "<test.txt");
    while(<INPUT>){
        my($line) = $_;
        chomp($line);

        print "reading $line\n";

        if ($line =~ /X/){
            print "pushing $line\n";
            lock(@ary);
            push @ary, $line;
        }
        sleep(4)
    }
    close(INPUT);
}

Попробуйте использовать этот код в качестве файла test.txt:

line 1
line 2X
line 3
line 4X
line 5
line 6
line 7X
line 8
line 9
line 10
line 11
line 12X
0
ответ дан 8 December 2019 в 17:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: