Perl: Почему "last" не используется для выхода из grep или карты?
perldocs говорят о last
:
last
не может использоваться для выхода из блока, который возвращает значение, например eval {}
, sub {}
или do {}
, и не должен использоваться для выхода из операции grep() или map().
Почему этого следует избегать в grep() или map()? Мне особенно интересна карта, поскольку она является альтернативой конструкции foreach. Документы, похоже, настаивают на том, чтобы что-то не делать, не описывая последствий.
Ответы
Ответ 1
Сначала убедитесь, что grep
и map
прозрачны для next
, last
и redo
, как и if
.
$ perl -e'
print "A";
for (8,9) {
print "B";
print map { print "C"; next; print "D"; $_ } 1,2,3;
print "E";
}
print "F\n";
'
ABCBCF
Но документы не говорят, что last
не может использоваться для выхода из map
, он говорит, что он не должен использоваться. Неясно, что это значит.
- Не используйте их, думая, что они повлияют на
map
и grep
?
- Не используйте их таким образом, потому что ваш читатель может подумать, что они влияют на
map
и grep
?
- Не используйте их так, потому что он может оставить Perl в состоянии ожидания?
- Не используйте их таким образом, потому что поведение может измениться в будущем?
Я не знаю.
Я не думаю, что это # 3, потому что я не знаю о каких-либо плохих побочных эффектах использования next
, last
и redo
, чтобы оставить обратный вызов map
или grep
.
Самое смешное, оставляя map
и grep
, используя конструкцию цикла, даже не предупреждает, даже если оставить sub в том же стиле.
$ perl -wE'while (1) { map { last; } 1; }'
$ perl -wE'while (1) { sub { last; }->(); }'
Exiting subroutine via last at -e line 1.
Ответ 2
map
предназначен для отображения одного списка в другой. Это, безусловно, не альтернатива foreach
. Я часто видел такие вещи, как
map { print "$_\n" } @data;
который совершает грех при использовании map
для своих побочных эффектов и выбрасывает полученный список. Вы не стали бы использовать
my $x = 99;
sqrt(my $y = $x * $x);
print $y;
и вы получите предупреждение. По той же причине вы не используете map
, когда имеете в виду for
.
По практической причине рассмотрим
print "A";
print map { print "B"; last; print "C";} 1,2,3;
print "Z";
OUTPUT
AB
поэтому в этом случае last
завершает работу всей программы. В общем случае использование last
или next
внутри блока map
или grep
выходит из содержащего блока, если оно есть, иначе вся программа. Этого следует избегать, поэтому "last
... не следует использовать для выхода из операции grep() или map()".
Ответ 3
В Perl существует приличное количество функциональных перекрытий между for/foreach
, grep
и map
. Тем не менее, просто потому, что вы можете потенциально использовать их все для решения одной и той же проблемы, не означает, что они являются лучшим инструментом для этой конкретной работы (см.: Используя отвертку, чтобы набить гвоздь).
-
grep
: его цель - отфильтровать список и вернуть полученное подмножество.
-
map
: его цель - изменить список и вернуть полученный измененный список.
-
for/foreach
: Его цель - перебирать список и делать что-то.
Теперь вы можете выполнить любое использование grep
и map
с помощью for/foreach
. grep
и map
являются в основном синтаксическим сахаром для специфического использования обработки списка (for/foreach
), которые являются особенно распространенными и полезными. По дизайну, по соглашению и по лучшим практикам, они торгуют определенными вещами для повышения удобства и ясности.
Одна из этих вещей заключается в том, что grep
и map
предназначены для возврата значения. Использование их в пустотном контексте не рекомендуется и считается нарушением идиоматического Perl. Если вы не возвращаете значение, используйте for/foreach
. Если вы возвращаете значение, то last
прерывает этот процесс.
Если вы используете передовые методы для Perl, использование grep
и map
всегда будет возвращать значение, а это значит, что следует избегать использования last
.