2015-07-01 10 views
10

Dies funktioniert,Syntaxfehler, wenn Karte() gibt LIST

print map { $_." x" => $_ } 1..5; 
print map { ("$_ x" => $_) } 1..5; 
print map { ("$_ x") => $_ } 1..5; 

aber dies wirft Syntaxfehler,

print map { "$_ x" => $_ } 1..5; 

Ist dies dokumentiert Fehler, nicht dokumentierte Fehler, oder ich kann nicht sehen, warum das sollte nicht kompilieren?

Warum Perl denkt, das map EXPR, LIST statt map BLOCK LIST

+0

Ich glaube nicht, dass dies ein Fehler ist, nur ein Beispiel für Perl "Raten falsch" zwischen "map BLOCK LIST" und "map EXPR, LIST", die gegen Ende von "perldoc -f map" dokumentiert ist ] (http://perldoc.perl.org/functions/map.html). Hoffentlich kann jemand genau erklären, warum Perl in einer Antwort den Weg für Ihre Beispiele rät. – ThisSuitIsBlackNot

+0

@ThisSuitIsBlackNot Ja, es sieht so aus https://eval.in/390502 aber normalerweise 'map EXPR, LIST' benötigt' + 'vor EXPR. Ansonsten ist es ein BLOCK. –

+2

@ Сухой27 nein, map EXPR benötigt normalerweise kein +. Perl rät normalerweise gut. Hier sieht es so aus, als würden Sie einen anonymen Hash erstellen, keinen Block. – ysth

Antwort

3

Warum denkt Perl, dass dies map EXPR, LIST statt map BLOCK LIST sein sollte?

Der entsprechende Abschnitt des Codes ist in toke.c, Perl Lexer (die unten ist von 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; 
} 

Erklärung

Wenn der erste Term nach der Eröffnung curly ist eine Zeichenfolge (durch ', " oder ` abgegrenzt) oder ein Bareword, das mit einem Großbuchstaben beginnt, und der folgende Ausdruck lautet , oder => wird das Lockige als Anfang eines anonymen Hashes behandelt (das ist OPERATOR(HASHBRACK);).

Die anderen Fälle sind ein bisschen schwieriger für mich zu verstehen. Ich lief das folgende Programm durch gdb:

{ (x => 1) } 

und endete in der letzten else Block up:

else { 
    PL_lex_brackstack[PL_lex_brackets-1] = XSTATE; 
    PL_expect = XSTATE; 
} 

Es genügt zu sagen, der Ausführungspfad deutlich unterscheidet; Am Ende wird es als Block geparst.

+0

Schönes finden ... –

6

Von perlref

werden sollte, weil geschweiften Klammern (Klammern) für einige andere Dinge, einschließlich der Blöcke verwendet werden, können Sie gelegentlich Klammern eindeutig zu machen haben bei der Anfang einer Anweisung, indem ein + oder ein Return davor gesetzt wird, so dass Perl die öffnende Klammer erkennt, startet keinen BLOCK. Die Ökonomie und der mnemonische Wert der Verwendung von Curlies wird diesen gelegentlichen zusätzlichen Aufwand wert sein.

Um Ihre Absichten klarer zu machen und den Parser,

  • Say +{...} helfen eindeutig eine Hashreferenz

    @list_of_hashrefs = map +{ "$_ x" => $_ }, 1..5; 
    
  • Say {; ...} eindeutig einen Codeblock

    angeben angeben
    %mappings = map {; "$_ x" => $_ } 1..5; 
    
+0

Tnx, aber das erklärt nicht, warum ähnliche Konstrukte als BLOCK interpretiert werden, während ein bestimmtes als EXPR betrachtet wird. –