Perl 将回调转换为流

Perl 将回调转换为流,perl,inversion-of-control,Perl,Inversion Of Control,在Perl中,如何将需要回调的函数转换为返回结果流的新函数 图像我有一个固定的功能,无法更改: sub my_fixed_handler { my $callback = shift; my $count = 1; while(1) { $callback->($count++); } } 要打印所有数字,我可以轻松编写以下代码: my_fixed_handler( sub { my $num = shift; print

在Perl中,如何将需要回调的函数转换为返回结果流的新函数

图像我有一个固定的功能,无法更改:

sub my_fixed_handler {
    my $callback = shift;

    my $count = 1;
    while(1) {
       $callback->($count++);
    }
}
要打印所有数字,我可以轻松编写以下代码:

my_fixed_handler( sub {
    my $num = shift;
    print "...$num\n";
});
但是现在我需要另一个基于
my_fixed_handler
的函数,该函数只返回一个计算步骤的结果:

my $stream = my_wrapper( my_fixer_hander( ... ) ) ;
$stream->next;  # 1
$stream->next;  # 2

这是可能的吗?

使用管道满时阻塞的事实:在分叉进程中运行
fixed\u处理程序
,回调通过管道写回父级。当父级在读取后进行处理时,如果管道已满,则阻塞管道,写入程序正在等待。为了便于此操作,请编写一个额外的空字符串来填充管道

use warnings;
use strict;
use feature 'say';

sub fixed_handler {
    my $callback = shift;
    #state $count = 1; # would solve the problem
    my $count = 1;
    for (1..4) { $callback->($count++) }   
}

pipe my $reader, my $writer  or die "Can't open pipe: $!";
$writer->autoflush(1);
$reader->autoflush(1);

my $fill_buff = ' ' x 100_000;  # (64_656 - 3); # see text

my $iter = sub { 
    my $data = shift; 
    say "\twrite on pipe ... ($data)";
    say $writer $data;
    say $writer $fill_buff;     # (over)fill the buffer
};

my $pid = fork // die "Can't fork: $!";  #/

if ($pid == 0) {
    close $reader;
    fixed_handler($iter);
    close $writer;
    exit;
}

close $writer;
say "Parent: started kid $pid";

while (my $recd = <$reader>) {
    next if $recd !~ /\S/;      # throw out the filler
    chomp $recd;
    say "got: $recd";
    sleep 1;
}

my $gone = waitpid $pid, 0;
if    ($gone > 0) { say "Child $gone exited with: $?" }
elsif ($gone < 0) { say "No such process: $gone" }
与在命令行中找到的不同之处类似。即使这样,我仍然(有时)得到“双重写入”。虽然这可能不是一个阻碍,但我使用了
100K
来确保填充它

例如,有关缓冲区大小的讨论,请参阅

另一种方法是使用
IO::Handle::setvbuf
设置管道的缓冲区大小。然而,我遇到了“在这个体系结构上没有实现(在生产机器上),所以我不会考虑。

干扰缓冲当然会大大降低通信速度

这实现了作者评论中的想法



†对于“缓冲区”,我指的是管道的缓冲区(如果另一侧没有读取数据,则在管道块之前写入的数据量)。这是涉及到的其他缓冲区,但与此无关。

使用管道在满时阻塞的事实:在分叉过程中运行
fixed\u处理程序,回调通过管道写回父级。当父级在读取后进行处理时,如果管道已满,则阻塞管道,写入程序正在等待。为了便于此操作,请编写一个额外的空字符串来填充管道

use warnings;
use strict;
use feature 'say';

sub fixed_handler {
    my $callback = shift;
    #state $count = 1; # would solve the problem
    my $count = 1;
    for (1..4) { $callback->($count++) }   
}

pipe my $reader, my $writer  or die "Can't open pipe: $!";
$writer->autoflush(1);
$reader->autoflush(1);

my $fill_buff = ' ' x 100_000;  # (64_656 - 3); # see text

my $iter = sub { 
    my $data = shift; 
    say "\twrite on pipe ... ($data)";
    say $writer $data;
    say $writer $fill_buff;     # (over)fill the buffer
};

my $pid = fork // die "Can't fork: $!";  #/

if ($pid == 0) {
    close $reader;
    fixed_handler($iter);
    close $writer;
    exit;
}

close $writer;
say "Parent: started kid $pid";

while (my $recd = <$reader>) {
    next if $recd !~ /\S/;      # throw out the filler
    chomp $recd;
    say "got: $recd";
    sleep 1;
}

my $gone = waitpid $pid, 0;
if    ($gone > 0) { say "Child $gone exited with: $?" }
elsif ($gone < 0) { say "No such process: $gone" }
与在命令行中找到的不同之处类似。即使这样,我仍然(有时)得到“双重写入”。虽然这可能不是一个阻碍,但我使用了
100K
来确保填充它

例如,有关缓冲区大小的讨论,请参阅

另一种方法是使用
IO::Handle::setvbuf
设置管道的缓冲区大小。然而,我遇到了“在这个体系结构上没有实现(在生产机器上),所以我不会考虑。

干扰缓冲当然会大大降低通信速度

这实现了作者评论中的想法



†对于“缓冲区”,我指的是管道的缓冲区(如果另一侧没有读取数据,则在管道块之前写入的数据量)。这是涉及到的其他缓冲区,但与此无关。

您是否有权访问
$callback
?@simbabque他们(我们?)将
shift
插入
$callback
的匿名子项写入到
$callback
?这可能会有帮助,但只要
$count
我的\u fixed\u处理程序中是词法的,它就不会有多大帮助。每次你调用
my_fixed\u handler
它都会重新开始,所以每次都会用返回的东西包装它,给你
1
$callback
如何中断
while(1)
循环?您的
my_fixed_处理程序必须能够保持状态,以便将其用作迭代器。如果它确实是“固定的”,而您没有访问它的权限,那么回调可以接管它,但是
处理程序的工作是什么?您可以这样做,从
handler
获取逻辑并将其放入发送给它的回调中。然后处理程序每次只提供
1
。@zdim不,只是让它在写入管道时阻塞。你有权访问
$callback
?@simbabque他们(我们?)将
shift
插入
$callback
的匿名子脚本写入
$callback
?这可能会有帮助,但只要
$count
我的\u fixed\u处理程序中是词法的,它就不会有多大帮助。每次你调用
my_fixed\u handler
它都会重新开始,所以每次都会用返回的东西包装它,给你
1
$callback
如何中断
while(1)
循环?您的
my_fixed_处理程序必须能够保持状态,以便将其用作迭代器。如果它确实是“固定的”,而您没有访问它的权限,那么回调可以接管它,但是
处理程序的工作是什么?您可以这样做,从
handler
获取逻辑并将其放入发送给它的回调中。然后处理程序每次只提供
1
。@zdim不,只是让它在写入管道时阻塞。我还不能添加投票(还没有足够的特权),但感谢您的回答@谢谢,很高兴你喜欢:)。如果有问题,请告诉我。我还不能添加投票(还没有足够的特权),但谢谢你的回答@谢谢,很高兴你喜欢:)。如果有问题,请告诉我。
my $cnt; while (1) { ++$cnt; print $writer ' '; print "\r$cnt" } # reader sleeps