为什么我会从链接的地图调用中得到意想不到的结果?

时间:2010-01-18 18:54:19

标签: perl parsing map

我正在使用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 个答案:

答案 0 :(得分:10)

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块周围添加括号会改变代码解析的方式,导致每个{{1}}被单独处理而不是以菊花链形式处理。

答案 1 :(得分:6)

$ 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
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之后运行。< / p>

答案 2 :(得分:5)

我认为发生的事情是,如果没有parens,每个地图的输出都会作为参数提供给它之前的地图。假设您只有两张这样的地图:

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

最后一个执行第一个,输入Switch' - help',' - man'等.Twitch返回你期望的哈希值。然后,第一张地图执行,喂它切换'-c',' - m','s'和'-p'。但是它也会将第一个开关的结果输入它,这就解释了为什么你会得到HASH(...)不是有效选项名称的错误。

解决方案?使用parens使每个地图的参数显式[1],或使用多个推线,每个地图一个。

[1]如果你确实使用了parens,我建议不要(map ....)写地图(....),因为它会更清楚为什么那些parens。