При ответе на этот вопрос я пришел к пониманию, что не был уверен ли Perl map
может считаться циклом или нет?
С одной стороны, это шарлатаны/обходы как цикл (делает O (n) работа, может быть легко переписан эквивалентным циклом и видом соответствий общее определение = "последовательность инструкций, которая постоянно повторяется").
С другой стороны, map
обычно не перечисляется среди управляющих структур Perl, из которых циклы являются подмножеством. Например, http://en.wikipedia.org/wiki/Perl_control_structures#Loops
Так, что я ищу, формальная причина, которая будет убеждена в одной стороне по сравнению с другим. До сих пор первый (это - цикл) звучит намного более убедительным мне, но я побеспокоен тем, что я никогда не видел "карту", упомянутую в списке циклов Perl.
«Цикл» - это скорее термин CS, нежели специфичный для языка. Вы можете быть достаточно уверены в вызове чего-либо цикла, если он демонстрирует следующие характеристики:
O (n)
map
подходит это довольно близко, но это не цикл, потому что это абстракция более высокого уровня. Можно сказать, что у него есть свойства цикла, даже если он сам не является циклом в самом строгом, низкоуровневом смысле.
Функция map
не является циклом в Perl. Это ясно видно по неудачам next
, redo
и last
внутри карты
:
perl -le '@a = map { next if $_ %2; } 1 .. 5; print for @a'
Can't "next" outside a loop block at -e line 1.
Для достижения желаемого аффекта в карте
, вы должны вернуть пустой список:
perl -le '@a = map { $_ %2 ? () : $_ } 1 .. 5; print for @a'
2
4
Я думаю, что преобразование - лучшее название для таких конструкций, как карта
. Он преобразует один список в другой. Функция, аналогичная map
, - это List :: Util :: reduce
, но вместо преобразования списка в другой список она преобразует список в скалярное значение. Используя слово «преобразование», мы можем говорить об общих аспектах этих двух функций высшего порядка.
Тем не менее, он работает, посещая каждого члена списка. Это означает, что он ведет себя во многом как цикл, и в зависимости от вашего определения «цикла» он может быть квалифицирован. Обратите внимание, мое определение означает, что в этом коде также нет цикла:
#!/usr/bin/perl
use strict;
use warnings;
my $i = 0;
FOO:
print "hello world!\n";
goto FOO unless ++$i == 5;
Perl фактически определяет словарный цикл в своей документации:
loop
A construct that performs something repeatedly, like a roller
coaster.
Согласно этому определению, map
является цикл, потому что он многократно преформирует свой блок; однако он также определяет «оператор управления циклом» и «метку цикла»:
loop control statement
Any statement within the body of a loop that can make a loop
prematurely stop looping or skip an "iteration". Generally you
shouldn't try this on roller coasters.
loop label
A kind of key or name attached to a loop (or roller coaster) so
that loop control statements can talk about which loop they want to
control.
Я считаю неточным вызывать map
цикл, потому что next
и его родственники определены как операторы управления циклом , и они не могут управлять картой
.
Это все просто игра словами. Описание map
как петли - это совершенно верный способ познакомить кого-то с ней.Даже документация для map
использует цикл foreach
как часть своего примера:
%hash = map { get_a_key_for($_) => $_ } @array;
is just a funny way to write
%hash = ();
foreach (@array) {
$hash{get_a_key_for($_)} = $_;
}
Но все зависит от контекста. Кому-то полезно описывать умножение как повторяющееся сложение, когда вы пытаетесь заставить его или ее понять концепцию, но вы не хотите, чтобы он или она продолжали думать об этом таким образом. Вы бы хотели, чтобы он или она выучили правила умножения вместо того, чтобы всегда возвращаться к правилам сложения.
Ваш вопрос касается проблемы классификации. По крайней мере, согласно одной интерпретации, вопрос о том, является ли карта
циклом, подобен вопросу о том, является ли карта
подмножеством «Цикла». В такой постановке я думаю, что ответ отрицательный. Хотя map
и Loop имеют много общего, есть важные различия.
next
и last
, а map
- нет. map
- это возвращаемое значение; с петлями, не так уж и много. Мы постоянно сталкиваемся с подобными отношениями в реальном мире - вещами, которые имеют много общего друг с другом, но ни один из них не является идеальным подмножеством другого.
-----------------------------------------
|Things that iterate? |
| |
| ------------------ |
| |map() | |
| | | |
| | --------|---------- |
| | | | | |
| | | | | |
| ------------------ | |
| | | |
| | Loop| |
| ------------------ |
| |
-----------------------------------------
С академической точки зрения, в зависимости от того, как определяется карта, могут быть рассмотрены оба варианта. Если он всегда выполняет итерацию по порядку, то цикл foreach
может быть эмулирован с помощью map
, делающей два эквивалента. Некоторые другие определения map могут допускать выполнение списка вне очереди для повышения производительности (разделение работы между потоками или даже отдельными компьютерами). То же самое можно сделать с конструкцией foreach
.
Но что касается Perl 5, map
всегда выполняется по порядку, что делает его эквивалентным циклу. Внутренняя структура выражения map $ _ * 2, 1, 2, 3
приводит к следующим кодам операций порядка выполнения, которые показывают, что map
внутренне построена как while
-подобная структура управления:
OP enter
COP nextstate
OP pushmark
SVOP const IV 1
SVOP const IV 2
SVOP const IV 3
LISTOP mapstart
LOGOP (0x2f96150) mapwhile <-- while still has items, shift one off into $_
PADOP gvsv GV *_
SVOP const IV 2 loop body
BINOP multiply
goto LOGOP (0x2f96150) <-- jump back to the top of the loop
LISTOP leave
карта - это концепция более высокого уровня, чем циклы, заимствованная из функционального программирования. Он не говорит «вызовите эту функцию для каждого из этих элементов, один за другим, от начала до конца», он говорит: «вызывайте эту функцию для всех этих элементов». Он может быть реализован как цикл, но не в этом суть - он также может быть реализован асинхронно - это все равно будет карта.
Кроме того, это не совсем структура управления сама по себе - что, если бы каждая функция Perl, которая использовала цикл в своей реализации, была указана в разделе «циклы»? Тот факт, что что-то реализовано с использованием цикла, не означает, что это следует рассматривать как отдельный тип цикла.
- это функция высшего порядка . То же самое и с grep. Книга Perl высшего порядка объясняет идею во всех деталях.
Печально видеть, что обсуждение перешло к деталям реализации, а не к концепции.
Нет, с моей точки зрения, это не петля.
Циклы (perl) характеризуются тем, что они могут быть прерваны ( last
) или возобновлены ( next
, redo
). map
не может:
map { last } qw(stack overflow); # ERROR! Can't "last" outside a loop block
Сообщение об ошибке предполагает, что сам perl не считает оцениваемый блок блоком цикла .
Я думаю о map как о более близком операторе, например, умножения. Можно даже представить целочисленное умножение как цикл сложений :). Это, конечно, не цикл, даже если бы он был глупо реализован таким образом. Я вижу карту аналогично.
Сама карта
обычно реализуется с использованием цикла некоторого вида (обычно для перебора итераторов), но поскольку это структура более высокого уровня, она часто не включается в списки более низких -уровневые управляющие структуры.
Все зависит от того, как вы на это смотрите...
С одной стороны, map
языка Perl можно считать циклом, хотя бы потому, что именно так он реализован в (текущих версиях) Perl.
С другой стороны, я рассматриваю его как функциональный map
и предпочитаю использовать его соответствующим образом, что, среди прочего, включает только предположение, что все элементы списка будут посещены, но не делает никаких предположений о порядке, в котором они будут посещены. Помимо того, что это обеспечивает функциональную чистоту и дает map
причину для существования и использования вместо for
, это также оставляет меня в хорошей форме, если в какой-то будущей версии Perl появится распараллеливаемая реализация map
. (Не то чтобы я надеялся, что это когда-нибудь произойдет...)
Ответы FM и Дэйва Шерохмана довольно хороши, но позвольте мне добавить дополнительный способ просмотра карты.
map - это функция, которая гарантированно просматривает каждый элемент структуры ровно один раз . И это не структура управления , поскольку она (сама) является чистой функцией. Другими словами, инварианты , которые сохраняет отображение, очень сильны, намного сильнее, чем «петля». Так что, если вы можете использовать карту, это здорово, потому что тогда вы получаете все эти инварианты «бесплатно», а если вы используете (более общую!) Структуру управления, вам придется установить все эти инварианты самостоятельно, если вы хочу быть уверенным, что ваш код верен.
И в этом действительно прелесть многих функций высшего порядка: вы получаете гораздо больше инвариантов бесплатно, так что вы, как программист, можете тратить свое драгоценное время на размышления, поддерживая инварианты, зависящие от приложения, вместо того, чтобы беспокоиться о низкоуровневых инвариантах. вопросы, связанные с реализацией.
Вот определение map как рекурренции
:
sub _map (&@) {
my $f = shift;
return unless @_;
return $f->( local $_ = shift @_ ),
_map( $f, @_ );
}
my @squares = _map { $_ ** 2 } 1..100;