Как я могу предотвратить чтение perl после конца связанного массива, который уменьшается при доступе?
Есть ли способ заставить Perl звонить FETCHSIZE
в связанном массиве перед каждым вызовом 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
)
2 ответа
sub FETCH {
my ($self, $index) = @_;
my $size = $self->FETCHSIZE;
...
}
Та да!
Я подозреваю, что вам не хватает, они просто методы. Методы, вызываемые с помощью магии связи, но все же просто методы, которые вы можете вызывать сами.
Распечатка содержимого связанного массива сводится к следующему:
my @array;
my $tied_obj = tied @array;
for my $idx (0..$tied_obj->FETCHSIZE-1) {
push @array, $tied_obj->FETCH($idx);
}
return @array;
Таким образом, у вас нет возможности контролировать количество итераций. Не может FETCH
достоверно сказать, если это вызывается из @array
или же $array[$idx]
или же @array[@idxs]
, Это отстой. Связи отстой, и они очень медленные. Примерно в 3 раза медленнее, чем обычный вызов метода, и в 10 раз, чем обычный массив.
Ваш пример уже разрушает ожидания относительно массивов (10 элементов входят, 5 элементов выходят). Что происходит, когда пользователь запрашивает $array[3]
? Они получают undef
? Альтернативы включают просто использование объектного API, если ваша вещь не ведет себя точно так же, как притворяющийся массив, это только добавит путаницы. Или вы можете использовать объект с перегруженным массивом deref.
Итак, то, что вы делаете, может быть сделано, но трудно заставить его работать хорошо. Что вы действительно пытаетесь достичь?
Я думаю, что порядок, в котором 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
}