【问题标题】:Cloning a generator in perl (Coro::Generator package)在 perl 中克隆生成器(Coro::Generator 包)
【发布时间】:2014-04-10 19:39:58
【问题描述】:

我想在 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 ...

非常感谢任何帮助。

【问题讨论】:

    标签: perl iterator clone generator


    【解决方案1】:

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

    1. 模块本身有一种相当老套的感觉
    2. 计算下一个生成的项目可能会产生副作用。因此,通常不可能“拆分”迭代器。

    但是,我们可以通过缓存迭代器的结果来“克隆”自己:

    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
    

    【讨论】:

    • 谢谢。但是如果迭代器需要一段时间才能回到相同的位置,这个操作可能会很昂贵。
    猜你喜欢
    • 2014-05-16
    • 2017-07-24
    • 2014-11-28
    • 1970-01-01
    • 1970-01-01
    • 2017-07-28
    • 2015-08-31
    • 1970-01-01
    相关资源
    最近更新 更多