2010-04-29 2 views
6

Есть ли способ, чтобы подпрограмма отправляла данные обратно при обработке? Например (этот пример используется просто для иллюстрации) - подпрограмма читает файл. Пока он читает файл, если выполняется какое-то условие, то «возвращать» эту строку и продолжать обрабатывать. Я знаю, что есть те, на которые ответят - зачем вы хотите это сделать? и почему бы вам не просто ...?, но я действительно хотел бы знать, возможно ли это.Может ли подпрограмма Perl возвращать данные, но продолжать обработку?

+1

Можете ли вы уточнить, что вы подразумеваете под «возвратом одной строки и продолжением обработки»? Вы имеете в виду одновременно с обработкой возвращенной строки? Вы имеете в виду, что рутина берет верх, где она остановилась? – frankc

+3

Это очень неясно. Как правило, существует один поток выполнения, и в этом случае он либо находится в вызывающем, либо вызываемом абоненте, поэтому возвращение и непрерывная обработка невозможны. Вы говорите об использовании потоков? –

+1

Я считаю, что он описывает сопрограммы. Некоторые языки реализуют это с помощью выражения, называемого «yield» или «yield return», которое возвращает значение вызывающему, а также вызывает следующий вызов функции: * resume *, выполняющийся с точки урожая, при этом все локальные переменные остаются нетронутыми , –

ответ

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 @_}; 
+1

@ Эрик: Что с закрытием в начале? – Zaid

+0

Это не возвращает данные на более высокий уровень. Это просто подпрограмма, вызывающая другую подпрограмму. –

+0

просто для хорошей меры, так как '$ log' используется только' log_line', нет причин для его наличия области видимости –

0

Если вы действительно этого хотите, вы можете использовать потоки. Один из вариантов заключается в том, чтобы развернуть отдельный поток, который читает файл, и когда он найдет определенную строку, поместите его в массив, общий для потоков. Затем другой поток может принимать линии, как они найдены, и обрабатывать их. Вот пример, который читает файл, ищет «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 
4

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

+2

Вид. Генераторы прекращают обработку, когда возвращают результат, и начинают снова, когда запрашивается следующий результат. В них нет ничего, что создаст второй поток выполнения. – mob

+2

@mobrule: вопрос не упоминал темы, а просто «продолжал обрабатывать». Генераторы позволяют локальному контексту генератора сохраняться между вызовами, которые можно рассматривать как одно и то же (на концептуальном уровне). –

+0

Правда. Неясно, что означает OP, «продолжайте обработку». – mob

2

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

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

Это похоже на сложную версию 'while (<$fh>) {next except/foo /; ...} '. Обратите внимание на завершение обработки итератора обработчиком итератора. Он не продолжал обрабатывать. –

+0

@brian: На этом простом примере да, ваш код короче и проще, но закрытие дает гораздо большую гибкость. Не так сложно реорганизовать выше, чтобы функции преобразования последовательности (например, фильтры, подобные описанным выше, но также и мутаторы и генераторы) могли быть соединены вместе или объединены в la LINQ. Эффект похож на карту и т. Д., Но с тем отличием, что оценка может быть «приостановлена» в любое время. –

+0

Я не спорю с закрытием. Вы обнаружите, что многие из моих модулей CPAN делают только то, что вы говорите, и я рассказываю о том, что вы упомянули в нескольких главах Mastering Perl. Тем не менее, они по-прежнему являются только подпрограммами, которые вы вызываете, и чье возвращаемое значение вы используете. За этим нет особой магии. –

-1

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

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

(Это 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(); 
+0

Уход за комментарием, downvoter? –

+2

Возможно, это связано с тем, что вопрос помечен 'perl', и ваш ответ предоставляет реализацию javascript. – dreamlax

+0

Он говорит псевдокод, не знаю синтаксиса в perl, чтобы сделать это. (Havent использует perl за столько лет.) –

-1

насчет рекурсивного суб? Re-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); 
+0

Если файл большой, это очень быстро съедет ваш стек вызовов. исправить последнюю строку 'process_file' с помощью:' goto & process_file, если eof $ fileHandle; 'для устранения роста стека во время хвостовой рекурсии –

+0

@Eric: Реализовано – Zaid

+0

=> вам нужно удалить список аргументов, как показано в первом комментарии. синтаксис 'goto ⊂' будет вызывать '& sub' с текущим значением' @ _'. как и в настоящее время, он вызывается process_file, а затем, когда он вернется, perl попытается получить возвращаемое значение в качестве метки. –

3

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