Почему я получаю неожиданные результаты от связанных вызовов на карту? - PullRequest
2 голосов
/ 18 января 2010

Я использую Getopt :: Lucid для обработки CLO и столкнулся с интересной и неожиданной проблемой. Следующий код:

push @clo_spec, map { Switch($_) } qw(-c -m -s -p),
                map { Switch($_) } qw(--help --man --usage --version),
                map { Switch($_) } qw(--debug --verbose),
                map { Param($_)  } keys %$rc_spec_ref
;

my $clo_o = Getopt::Lucid->getopt(\@clo_spec);

генерирует следующую ошибку:

'Getopt::Lucid::Spec=HASH(0x9383847)' is not a valid option name/alias

Теперь Getopt :: Lucid настраивается путем цитирования строкового выражения, представляющего допустимые параметры, а затем передачи этих строк одной из шести подпрограмм, которые возвращают благословенные хэши. Каждая подпрограмма представляет тип опции; переключатель, счетчик, параметр, список или пара ключей.

Интересная часть состоит в том, что если удаляются любые три выражения карты,

push @clo_spec, #map { Switch($_) } qw(-c -m -s -p),
                map { Switch($_) } qw(--help --man --usage --version),
                #map { Switch($_) } qw(--debug --verbose),
                #map { Param($_)  } keys %$rc_spec_ref
;

тогда все отлично работает. Еще более интересная часть заключается в том, что если вы заключите каждое выражение карты в скобки, все также будет работать нормально:

push @clo_spec, (map { Switch($_) } qw(-c -m -s -p)),
                (map { Switch($_) } qw(--help --man --usage --version)),
                (map { Switch($_) } qw(--debug --verbose)),
                (map { Param($_)  } keys %$rc_spec_ref)
;

Вышесказанное наводит меня на мысль, что эта проблема не связана с ошибкой в ​​Getopt :: Lucid. Кроме того, я рассмотрел вышеупомянутое исправление после просмотра ссылки на функцию карты, в которой упоминается, что иногда карту можно спутать с запятыми. Perl сгладит встроенные списки, и окружающие скобки, по-видимому, разграничивают каждое выражение карты, но я действительно не понимаю, что происходит.

Может кто-нибудь объяснить, пожалуйста?

Ответы [ 3 ]

10 голосов
/ 18 января 2010

Функция map принимает список в качестве аргумента и генерирует список в качестве результата.Вы можете связать map операторы вместе (передавая выходные данные одного map как входные данные другому), что и делает ваш первый пример.Добавление скобок вокруг отдельных map операторов разрывает цепочку.

При чтении цепочечных map (или grep) операторов читайте справа налево.

push @clo_spec,
    map { Switch($_) } qw(-c -m -s -p),
    map { Switch($_) } qw(--help --man --usage --version),
    map { Switch($_) } qw(--debug --verbose),
    map { Param($_)  } keys %$rc_spec_ref;

Последнийmap вызывает Param() для каждого ключа из %$rc_spec_ref и возвращает результаты.map выше, который вызывает Switch() для значений --debug, --verbose, и каждого результата из последнего map.Блоки map над ними получают еще более длинные списки аргументов с флагами в qw(), в которых содержатся результаты объединения других блоков map.

Добавление скобок вокруг каждого блока map меняет способкод разбирает, в результате чего каждый map обрабатывается индивидуально, а не цепочкой.

6 голосов
/ 18 января 2010
$ <b>perl -MO=Deparse
push @clo_spec, map { Switch($_) } qw(-c -m -s -p),
                map { Switch($_) } qw(--help --man --usage --version),
                map { Switch($_) } qw(--debug --verbose),
                map { Param($_)  } keys %$rc_spec_ref
;

my $clo_o = Getopt::Lucid->getopt(\@clo_spec);
^D</b>
push @clo_spec, map({Switch($_);} ('-c', '-m', '-s', '-p'), map({Switch($_);} (
'--help', '--man', '--usage', '--version'), map({Switch($_);} ('--debug',
'--verbose'), map({Param($_);} keys %$rc_spec_ref))));
my $clo_o = 'Getopt::Lucid'->getopt(\@clo_spec);
- syntax OK

Если вас когда-нибудь смущает то, как Perl что-то анализирует, B :: Deparse просто потрясающе.

В этом случае вы можете ясно видеть, что каждый map работает не только с указанным вами qw(), но также с результатами map, следующими за ним.

5 голосов
/ 18 января 2010

Я думаю, что происходит то, что без паренов выходные данные каждой карты передаются в качестве аргументов для карты, предшествующей ей. Скажем, у вас было только две из этих карт:

push @clo_spec, (map { Switch($_) } qw(-c -m -s -p)),
                (map { Switch($_) } qw(--help --man --usage --version)),

Последний выполняется первым, передавая Switch '--help', '--man' и т. Д. Switch возвращает ожидаемые хэши. Затем выполняется первая карта, передавая ей ключи '-c', '-m', 's' и '-p'. Но затем он также передает результат первого переключения, который объясняет, почему вы получаете ошибки, что HASH (...) не является допустимым именем опции.

Решение? Либо используйте парены, чтобы сделать аргументы каждой карты явными [1], либо используйте несколько строк push, по одной для каждой карты.

[1] Если вы используете парены, я бы порекомендовал вместо (карты ....) писать карту (....), так как было бы более понятно, почему парены там.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...