4

有什么方法可以强制 PerlFETCHSIZE在每次调用之前调用绑定数组FETCH?我的绑定数组知道它的最大大小,但可能会根据之前FETCH调用的结果从这个大小缩小。这是一个人为的示例,它将列表过滤为仅具有惰性求值的偶数元素:

use warnings;
use strict;

package VarSize;

sub TIEARRAY { bless $_[1] => $_[0] }
sub FETCH {
    my ($self, $index) = @_;
    splice @$self, $index, 1 while $$self[$index] % 2;
    $$self[$index]
}
sub FETCHSIZE {scalar @{$_[0]}}

my @source = 1 .. 10;

tie my @output => 'VarSize', [@source];

print "@output\n";  # array changes size as it is read, perl only checks size
                    # at the start, so it runs off the end with warnings
print "@output\n";  # knows correct size from start, no warnings

为简洁起见,我省略了一堆错误检查代码(例如如何处理从 0 以外的索引开始的访问)

编辑:而不是上面的两个打印语句,如果使用以下两行之一,第一行将正常工作,第二行将引发警告。

print "$_ " for @output;   # for loop "iterator context" is fine,
                           # checks FETCHSIZE before each FETCH, ends properly

print join " " => @output; # however a list context expansion 
                           # calls FETCHSIZE at the start, and runs off the end

更新:

实现可变大小绑定数组的实际模块称为List::Gen,它位于 CPAN 上。该函数的filter行为类似于grep,但与List::Gen的惰性生成器一起使用。有没有人有任何想法可以使实施filter更好?

test功能类似,但undef在失败的槽中返回,保持数组大小不变,但当然与使用语义不同grep

4

2 回答 2

1
sub FETCH {
    my ($self, $index) = @_;
    my $size = $self->FETCHSIZE;
    ...
}

Ta da!

I suspect what you're missing is they're just methods. Methods called by tie magic, but still just methods you can call yourself.

Listing out the contents of a tied array basically boils down to this:

my @array;
my $tied_obj = tied @array;
for my $idx (0..$tied_obj->FETCHSIZE-1) {
    push @array, $tied_obj->FETCH($idx);
}

return @array;

So you don't get any opportunity to control the number of iterations. Nor can FETCH reliably tell if its being called from @array or $array[$idx] or @array[@idxs]. This sucks. Ties kinda suck, and they're really slow. About 3 times slower than a normal method call and 10 times than a regular array.

Your example already breaks expectations about arrays (10 elements go in, 5 elements come out). What happen when a user asks for $array[3]? Do they get undef? Alternatives include just using the object API, if your thing doesn't behave exactly like an array pretending it does will only add confusion. Or you can use an object with array deref overloaded.

So, what you're doing can be done, but its difficult to get it to work well. What are you really trying to accomplish?

于 2009-12-21T20:55:21.283 回答
0

我认为 perl 调用FETCH/FETCHSIZE方法的顺序不能改变。它是 perls 的内部部分。为什么不直接删除警告:

sub FETCH {
    my ($self, $index) = @_;
    splice @$self, $index, 1 while ($$self[$index] || 0) % 2;
    exists $$self[$index] ? $$self[$index] : '' ## replace '' with default value
}
于 2009-12-21T22:37:30.250 回答