这有效,
print map { $_." x" => $_ } 1..5;
print map { ("$_ x" => $_) } 1..5;
print map { ("$_ x") => $_ } 1..5;
Run Code Online (Sandbox Code Playgroud)
但这会引发语法错误,
print map { "$_ x" => $_ } 1..5;
Run Code Online (Sandbox Code Playgroud)
这是有记录的bug,没有文档的bug,或者我不明白为什么这不应该编译?
为什么perl认为这应该是map EXPR, LIST代替map BLOCK LIST
从 perlref
因为大括号(大括号)用于包括BLOCK在内的其他几个东西,你可能偶尔必须通过在前面放一个+或一个返回来消除语句开头的大括号,以便Perl意识到左大括号没有启动BLOCK .使用curlies的经济和记忆价值被认为值得偶尔额外麻烦.
为了让你的意图更清晰并帮助解析器,
说+{...}明确指定哈希引用
@list_of_hashrefs = map +{ "$_ x" => $_ }, 1..5;
Run Code Online (Sandbox Code Playgroud)说{; ...}明确指定一个代码块
%mappings = map {; "$_ x" => $_ } 1..5;
Run Code Online (Sandbox Code Playgroud)为什么 perl 认为这应该
map EXPR, LIST代替map BLOCK LIST?
相关代码部分位于toke.cPerl 的词法分析器中(以下来自 Perl 5.22.0):
/* This hack serves to disambiguate a pair of curlies
* as being a block or an anon hash. Normally, expectation
* determines that, but in cases where we're not in a
* position to expect anything in particular (like inside
* eval"") we have to resolve the ambiguity. This code
* covers the case where the first term in the curlies is a
* quoted string. Most other cases need to be explicitly
* disambiguated by prepending a "+" before the opening
* curly in order to force resolution as an anon hash.
*
* XXX should probably propagate the outer expectation
* into eval"" to rely less on this hack, but that could
* potentially break current behavior of eval"".
* GSAR 97-07-21
*/
t = s;
if (*s == '\'' || *s == '"' || *s == '`') {
/* common case: get past first string, handling escapes */
for (t++; t < PL_bufend && *t != *s;)
if (*t++ == '\\')
t++;
t++;
}
else if (*s == 'q') {
if (++t < PL_bufend
&& (!isWORDCHAR(*t)
|| ((*t == 'q' || *t == 'x') && ++t < PL_bufend
&& !isWORDCHAR(*t))))
{
/* skip q//-like construct */
const char *tmps;
char open, close, term;
I32 brackets = 1;
while (t < PL_bufend && isSPACE(*t))
t++;
/* check for q => */
if (t+1 < PL_bufend && t[0] == '=' && t[1] == '>') {
OPERATOR(HASHBRACK);
}
term = *t;
open = term;
if (term && (tmps = strchr("([{< )]}> )]}>",term)))
term = tmps[5];
close = term;
if (open == close)
for (t++; t < PL_bufend; t++) {
if (*t == '\\' && t+1 < PL_bufend && open != '\\')
t++;
else if (*t == open)
break;
}
else {
for (t++; t < PL_bufend; t++) {
if (*t == '\\' && t+1 < PL_bufend)
t++;
else if (*t == close && --brackets <= 0)
break;
else if (*t == open)
brackets++;
}
}
t++;
}
else
/* skip plain q word */
while (t < PL_bufend && isWORDCHAR_lazy_if(t,UTF))
t += UTF8SKIP(t);
}
else if (isWORDCHAR_lazy_if(t,UTF)) {
t += UTF8SKIP(t);
while (t < PL_bufend && isWORDCHAR_lazy_if(t,UTF))
t += UTF8SKIP(t);
}
while (t < PL_bufend && isSPACE(*t))
t++;
/* if comma follows first term, call it an anon hash */
/* XXX it could be a comma expression with loop modifiers */
if (t < PL_bufend && ((*t == ',' && (*s == 'q' || !isLOWER(*s)))
|| (*t == '=' && t[1] == '>')))
OPERATOR(HASHBRACK);
if (PL_expect == XREF)
{
block_expectation:
/* If there is an opening brace or 'sub:', treat it
as a term to make ${{...}}{k} and &{sub:attr...}
dwim. Otherwise, treat it as a statement, so
map {no strict; ...} works.
*/
s = skipspace(s);
if (*s == '{') {
PL_expect = XTERM;
break;
}
if (strnEQ(s, "sub", 3)) {
d = s + 3;
d = skipspace(d);
if (*d == ':') {
PL_expect = XTERM;
break;
}
}
PL_expect = XSTATE;
}
else {
PL_lex_brackstack[PL_lex_brackets-1] = XSTATE;
PL_expect = XSTATE;
}
Run Code Online (Sandbox Code Playgroud)
如果开头大写字母后面的第一个术语是字符串(由'、"或分隔`)或以大写字母开头的裸字,并且后面的术语是,或=>,则大写字母被视为匿名散列的开头(这就是意思OPERATOR(HASHBRACK);) 。
其他情况对我来说有点难以理解。我通过gdb运行了以下程序:
{ (x => 1) }
Run Code Online (Sandbox Code Playgroud)
并最终进入最后一个else区块:
{ (x => 1) }
Run Code Online (Sandbox Code Playgroud)
可以说,执行路径明显不同;它最终被解析为一个块。