Подтвердить что ты не робот

Perl: Почему "last" не используется для выхода из grep или карты?

perldocs говорят о last:

last не может использоваться для выхода из блока, который возвращает значение, например eval {}, sub {} или do {}, и не должен использоваться для выхода из операции grep() или map().

Почему этого следует избегать в grep() или map()? Мне особенно интересна карта, поскольку она является альтернативой конструкции foreach. Документы, похоже, настаивают на том, чтобы что-то не делать, не описывая последствий.

4b9b3361

Ответ 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.