Perl子程序可以返回数据但是继续处理吗?

时间:2010-04-29 20:00:22

标签: perl parallel-processing return-value subroutine

有没有办法让子程序在处理时发回数据?例如(此示例仅用于说明) - 子例程读取文件。当它正在读取文件时,如果满足某些条件,则“返回”该行并继续处理。我知道有些人会回答 - 你为什么要那样做?你为什么不......?,但我真的想知道这是否可能。

7 个答案:

答案 0 :(得分: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 :(得分:4)

有些语言使用"generators""coroutines"提供此类功能,但Perl却没有。上面链接的生成器页面包含Python,C#和Ruby(以及其他)中的示例。

答案 2 :(得分:3)

Coro模块看起来对这个问题很有用,虽然我不知道它是如何工作的,也不知道它是否做了它所宣传的内容。

答案 3 :(得分: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
}

答案 4 :(得分: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

答案 5 :(得分:-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();

答案 6 :(得分:-1)

递归子怎么样?重新open现有的文件句柄不会重置输入行号,因此它会从它停止的位置继续。

以下是process_file子例程打印出包含foo的空白行分隔的"\n\n"段落的示例。

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);