処理中にサブルーチンにデータを送り返す方法はありますか?たとえば(この例は単に説明するために使用されています)-サブルーチンはファイルを読み取ります。ファイルの読み取り中に何らかの条件が満たされた場合は、その行を「返し」、処理を続行します。私は答える人がいることを知っています-なぜあなたはそれをしたいのですか?そして、なぜあなたは...?だけではありませんが、私は本当にこれが可能かどうか知りたいです。
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 @_};
Coroモジュールは、この問題に役立つように見えますが、どのように機能するのか、また、宣伝どおりに機能するのかどうかはわかりません。
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
}
本当にこれを実行したい場合は、スレッドを使用して実行できます。1つのオプションは、ファイルを読み取る別のスレッドをフォークし、特定の行が見つかったら、スレッド間で共有される配列に配置することです。次に、他のスレッドは、見つかった行を取得して処理します。これは、ファイルを読み取り、ファイルの行で「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
再帰的な潜水艦はどうですか?既存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);
あなたの言語がクロージャをサポートしている場合、あなたはこのようなことをすることができるかもしれません:
ちなみに、この関数はファイルの処理を継続せず、呼び出したときに実行されるため、必要なものではない可能性があります。
(これは擬似コードのような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();