克隆perl中的生成器(Coro :: Generator包)

时间:2014-03-07 15:12:17

标签: perl iterator clone generator

我想在Perl中克隆一个生成器(Coro :: Generator)状态。例如:在下面的代码中,我有一个迭代数组的生成器:

use Coro::Generator;
sub iterate_array {
    my @arr = @{ $_[0] };
    generator {
        foreach my $value (@arr) {
            # Each time function is called it returns the next value in the array
            yield $value;   
        }
        yield undef;
    };
}     

my @a = (1,2,3,4);
my $iterator = iterate_array(\@a);

print $iterator->();  # output: 1

因此输出为1.如果再次调用则为2(数组中的第2项)。

我现在想要克隆迭代器的状态。类似的东西:

my $clone = clone($iterator);          # Clone iterator
print $iterator->();                   # output: 2
print $clone->();                      # output: 2

$ clone和$ iterator产生的结果与它们的克隆相同。

我尝试过使用Storable包,即:

use Storable 'dclone';
my $clone = dclone($iterator);

但这会产生错误:

Can't store CODE items at ...

任何帮助都非常感激。

1 个答案:

答案 0 :(得分:0)

Coro::Generator模块预计不会克隆迭代器:

  1. 模块本身有一种相当hack的感觉
  2. 计算下一个生成的项目可能会产生副作用。因此通常不可能“拆分”迭代器。
  3. 但是,我们可以通过缓存迭代器的结果来“克隆”自己:

    sub clone_iter {
        my ($n, $iter) = @_;
        my @caches;
    
        my $make_subiterator = sub {
            # each subiterator has its own cache of not-yet seen items
            my $cache = [];
            push @caches, $cache;
            return sub {
                # If *our* cache is empty…
                if (not @$cache) {
                    my $result = $iter->();
                    # add this result to *all* caches
                    push @$_, \$result for @caches;
                }
                return ${ shift @$cache };
            };
        };
    
        return map { $make_subiterator->() } 1 .. $n;
    }
    
    my $i = 1;
    my $iter = sub { $i++ }; # just for testing
    my ($iter_a, $iter_b, $iter_c) = clone_iter(3, $iter);
    say join ' ', map { $iter_a->() } 1 ..  3;
    say join ' ', map { $iter_b->() } 1 ..  7;
    say join ' ', map { $iter_a->() } 4 .. 10;
    say join ' ', map { $iter_c->() } 1 ..  7;
    

    示例输出:

    1 2 3
    1 2 3 4 5 6 7
    4 5 6 7 8 9 10
    1 2 3 4 5 6 7