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

gvk*_*vkv 2 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);
Run Code Online (Sandbox Code Playgroud)

生成以下错误:

'Getopt::Lucid::Spec=HASH(0x9383847)' is not a valid option name/alias
Run Code Online (Sandbox Code Playgroud)

现在,通过引用表示有效选项的字符串表达式来配​​置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
;
Run Code Online (Sandbox Code Playgroud)

一切正常.更有趣的是,如果你将每个地图表达式括在括号中,一切都可以正常工作:

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)
;
Run Code Online (Sandbox Code Playgroud)

上面让我相信这个问题与Getopt :: Lucid中的错误无关.此外,我在查看地图函数的参考后考虑了上述修复,其中提到有时地图可能会与逗号混淆.Perl将展平嵌入列表,周围的括号似乎具有描绘每个地图表达式的效果,但我真的不明白发生了什么.

有人可以解释一下吗?

Mic*_*man 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;
Run Code Online (Sandbox Code Playgroud)

最后一次map调用Param()每个键%$rc_spec_ref并返回结果.在map调用上面Switch()的价值观--debug,--verbose,并从去年每个结果map.map上面的块会获得更长的参数列表,其中的标志qw()将其他map块的结果与它们连接起来.

在每个map块周围添加括号会改变代码解析的方式,导致每个map都被单独处理而不是以菊花链形式进行处理.

  • 关联性适用于二元运算符(以中缀表示法).`map`和`grep`是函数.Perl允许您省略大多数内置函数的括号.当您这样做时,函数的原型确定表达式如何解析.`map`的原型说第一个参数是要执行的块,其他一切都是要使用的数据.数据元素的数量不固定,因此除非使用`map(...)`语法明确界定参数列表,否则Perl会消耗所有内容. (2认同)

eph*_*ent 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以下操作的结果.


zig*_*don 5

我认为正在发生的是,没有parens,每个map的输出都作为参数提供给它之前的地图.假设您只有两张这样的地图:

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

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

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

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