Есть ли способ, чтобы подпрограмма отправляла данные обратно при обработке? Например (этот пример используется просто для иллюстрации) - подпрограмма читает файл. Пока он читает файл, если выполняется какое-то условие, то «возвращать» эту строку и продолжать обрабатывать. Я знаю, что есть те, на которые ответят - зачем вы хотите это сделать? и почему бы вам не просто ...?, но я действительно хотел бы знать, возможно ли это.Может ли подпрограмма Perl возвращать данные, но продолжать обработку?
ответ
Обычный способ реализовать этот тип функциональности с функцией обратного вызова:
{
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 @_};
@ Эрик: Что с закрытием в начале? – Zaid
Это не возвращает данные на более высокий уровень. Это просто подпрограмма, вызывающая другую подпрограмму. –
просто для хорошей меры, так как '$ log' используется только' log_line', нет причин для его наличия области видимости –
Если вы действительно этого хотите, вы можете использовать потоки. Один из вариантов заключается в том, чтобы развернуть отдельный поток, который читает файл, и когда он найдет определенную строку, поместите его в массив, общий для потоков. Затем другой поток может принимать линии, как они найдены, и обрабатывать их. Вот пример, который читает файл, ищет «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
Некоторые языки предлагают такого рода функции, используя "generators" или "coroutines", но Perl не делает. На приведенной выше странице генератора есть примеры в Python, C# и Ruby (среди прочих).
Вид. Генераторы прекращают обработку, когда возвращают результат, и начинают снова, когда запрашивается следующий результат. В них нет ничего, что создаст второй поток выполнения. – mob
@mobrule: вопрос не упоминал темы, а просто «продолжал обрабатывать». Генераторы позволяют локальному контексту генератора сохраняться между вызовами, которые можно рассматривать как одно и то же (на концептуальном уровне). –
Правда. Неясно, что означает OP, «продолжайте обработку». – mob
Самый простой способ сделать это в 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
}
Это похоже на сложную версию 'while (<$fh>) {next except/foo /; ...} '. Обратите внимание на завершение обработки итератора обработчиком итератора. Он не продолжал обрабатывать. –
@brian: На этом простом примере да, ваш код короче и проще, но закрытие дает гораздо большую гибкость. Не так сложно реорганизовать выше, чтобы функции преобразования последовательности (например, фильтры, подобные описанным выше, но также и мутаторы и генераторы) могли быть соединены вместе или объединены в la LINQ. Эффект похож на карту и т. Д., Но с тем отличием, что оценка может быть «приостановлена» в любое время. –
Я не спорю с закрытием. Вы обнаружите, что многие из моих модулей CPAN делают только то, что вы говорите, и я рассказываю о том, что вы упомянули в нескольких главах Mastering Perl. Тем не менее, они по-прежнему являются только подпрограммами, которые вы вызываете, и чье возвращаемое значение вы используете. За этим нет особой магии. –
Если ваш язык поддерживает закрытие, вы можете сделать что-то вроде этого:
Кстати, функция не будет продолжать обрабатывать файл, он будет запускаться только тогда, когда вы его вызываете, так что это может быть не то, что вам нужно.
(Это 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();
Уход за комментарием, downvoter? –
Возможно, это связано с тем, что вопрос помечен 'perl', и ваш ответ предоставляет реализацию javascript. – dreamlax
Он говорит псевдокод, не знаю синтаксиса в perl, чтобы сделать это. (Havent использует perl за столько лет.) –
насчет рекурсивного суб? 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);
Если файл большой, это очень быстро съедет ваш стек вызовов. исправить последнюю строку 'process_file' с помощью:' goto & process_file, если eof $ fileHandle; 'для устранения роста стека во время хвостовой рекурсии –
@Eric: Реализовано – Zaid
=> вам нужно удалить список аргументов, как показано в первом комментарии. синтаксис 'goto ⊂' будет вызывать '& sub' с текущим значением' @ _'. как и в настоящее время, он вызывается process_file, а затем, когда он вернется, perl попытается получить возвращаемое значение в качестве метки. –
Coro модуль выглядит как было бы полезно для этой проблемы, хотя я понятия не имею, как это работает и не идея делает ли это то, что она рекламирует.
Можете ли вы уточнить, что вы подразумеваете под «возвратом одной строки и продолжением обработки»? Вы имеете в виду одновременно с обработкой возвращенной строки? Вы имеете в виду, что рутина берет верх, где она остановилась? – frankc
Это очень неясно. Как правило, существует один поток выполнения, и в этом случае он либо находится в вызывающем, либо вызываемом абоненте, поэтому возвращение и непрерывная обработка невозможны. Вы говорите об использовании потоков? –
Я считаю, что он описывает сопрограммы. Некоторые языки реализуют это с помощью выражения, называемого «yield» или «yield return», которое возвращает значение вызывающему, а также вызывает следующий вызов функции: * resume *, выполняющийся с точки урожая, при этом все локальные переменные остаются нетронутыми , –