76 votes

Comment exactement R parse «-> », l’opérateur d’assignation de droit ?

Donc c'est une question banale, mais ça m'énerve que je ne peux pas répondre, et peut-être la réponse m'apprendra un peu plus de détails sur la façon de R fonctionne.

Le titre dit tout: comment R parse ->, l'obscur côté droit affectation de la fonction?

Mon habitude astuces de plonger dans cet échec:

> `->`

Erreur: objet -> non trouvé

> getAnywhere("->")

aucun objet nommé -> a été trouvé

Et nous ne pouvons pas l'appeler directement:

> `->`(3,x)

Erreur: impossible de trouver la fonction "->"

Mais bien sûr, ça fonctionne:

3 -> x #assigns the value 3 to the name x

Il semble R sait comment il vous suffit d'inverser les arguments, mais je pensais que les approches ci-dessus aurait sûrement craqué le cas:

> pryr::ast(3 -> y)
\- ()
  \- `<- #R interpreter clearly flipped things around
  \- `y  #  (by the time it gets to 'ast', at least...)
  \-  3  #  (note: this is because 'substitute(3 -> y)` 
         #   already returns the reversed version)

Comparez cela à l'opérateur d'affectation:

> `<-`
.Primitive("<-")

> `<-`(x, 3) #assigns the value 3 to the name x, as expected

?"->" , ?assignOps, et le Langage R Définition de tous il suffit de le mentionner, en passant, que le droit de l'opérateur d'affectation.

Mais il y a clairement quelque chose d'unique au sujet de combien de -> est utilisé. Ce n'est pas une fonction/opérateur (comme les appels à l' getAnywhere et directement à l' `->` semblent démontrer), alors quel est-il? Est-il complètement dans une classe de son propre chef?

Est-il quelque chose à apprendre de cette part "-> est complètement unique au sein de la R de la langue dans la façon dont elle est interprétée et de la manipulation, de mémoriser et de passer à autre chose"?

71voto

nrussell Points 10607

Permettez-moi de commencer par dire que je ne sais absolument rien sur la manière dont les analyseurs de travail. Cela dit, la ligne 296 de gramme.y définit les jetons suivants pour représenter affectation dans l' (YACC?) analyseur de R utilise:

%token      LEFT_ASSIGN EQ_ASSIGN RIGHT_ASSIGN LBB

Ensuite, sur les lignes 5140 grâce à 5150 de gramme.c, cela ressemble le correspondant du code C:

case '-':
  if (nextchar('>')) {
    if (nextchar('>')) {
      yylval = install_and_save2("<<-", "->>");
      return RIGHT_ASSIGN;
    }
    else {
      yylval = install_and_save2("<-", "->");
      return RIGHT_ASSIGN;
    }
  }

Enfin, à partir de la ligne de 5044 de gramme.c, la définition de l' install_and_save2:

/* Get an R symbol, and set different yytext.  Used for translation of -> to <-. ->> to <<- */
static SEXP install_and_save2(char * text, char * savetext)
{
    strcpy(yytext, savetext);
    return install(text);
}

Encore une fois, l'ayant aucune expérience de travail avec des analyseurs, il semble qu' -> et ->> sont directement traduits en <- et <<-, respectivement, à un niveau très faible dans le processus d'interprétation.


Vous apporté un très bon point de se demander comment parser "sait" pour inverser la arguments au -> - considérant qu' -> semble être installé dans le R de la table des symboles en tant que <- - et donc être capable d'interpréter correctement x -> y comme y <- x et pas x <- y. Le mieux que je puisse faire est de fournir plus de la spéculation que je continue à trouver des "preuves" à l'appui de mes affirmations. J'espère que certains miséricordieux YACC expert tomberez sur cette question et de fournir un peu de perspicacité; je ne vais pas retenir mon souffle sur que si.

Retour à la lignes 383 et 384 de gramme.y, cela ressemble plus à une logique d'analyse des LEFT_ASSIGN et RIGHT_ASSIGN symboles:

|   expr LEFT_ASSIGN expr       { $$ = xxbinary($2,$1,$3);  setId( $$, @$); }
|   expr RIGHT_ASSIGN expr      { $$ = xxbinary($2,$3,$1);  setId( $$, @$); }

Bien que je ne peux pas vraiment faire des têtes ou queues de ce fou de la syntaxe, j'ai remarqué que les deuxième et troisième arguments au xxbinary sont convertis en WRT LEFT_ASSIGN (xxbinary($2,$1,$3)) et RIGHT_ASSIGN (xxbinary($2,$3,$1)).

Voici ce que je nous vois dans ma tête:

LEFT_ASSIGN Scénario: y <- x

  • $2 est la deuxième "argument" à l'analyseur dans l'expression ci-dessus, c'est à dire <-
  • $1 est la première, à savoir y
  • $3 est le troisième; x

Par conséquent, la suite (C?) appel xxbinary(<-, y, x).

L'application de cette logique d' RIGHT_ASSIGN, c'est à dire x -> y, combiné avec ma conjecture sur <- et -> se sont inversées,

  • $2 se traduit d' -> de <-
  • $1 est x
  • $3 est y

Mais comme le résultat est xxbinary($2,$3,$1) au lieu de xxbinary($2,$1,$3), le résultat est toujours xxbinary(<-, y, x).


Profitant de cela un peu plus loin, nous avons la définition de l' xxbinary sur la ligne 3310 de gramme.c:

static SEXP xxbinary(SEXP n1, SEXP n2, SEXP n3)
{
    SEXP ans;
    if (GenerateCode)
    PROTECT(ans = lang3(n1, n2, n3));
    else
    PROTECT(ans = R_NilValue);
    UNPROTECT_PTR(n2);
    UNPROTECT_PTR(n3);
    return ans;
}

Malheureusement, je ne pouvais pas trouver une définition appropriée de l' lang3 (ou de ses variantes lang1, lang2, etc...) dans le R du code source, mais je suppose que c'est utilisé pour l'évaluation de fonctions spéciales (c'est à dire des symboles) d'une manière qui est synchronisé avec l'interprète.


Les mises à jour Je vais essayer de répondre à certaines de vos questions dans les commentaires du mieux que je peux donné mon (très) limité de connaissances de l'analyse de processus.

1) Est-ce vraiment le seul objet dans la R qui se comporte comme ça??? (J'ai obtenu dans l'esprit de John Chambers devis par Hadley du livre: "Tout qui existe, c'est un objet. Tout ce qui arrive est un appel de fonction." Ce bien se trouve en dehors de ce domaine -- est-il autre chose comme cette?

Tout d'abord, je suis d'accord que cela se trouve à l'extérieur de ce domaine. Je crois Chambres de devis concerne la R de l'Environnement, c'est à dire des processus qui sont tous de prendre place après ce faible niveau de l'analyse de phase. Je reviendrai un peu plus loin, cependant. De toute façon, le seul autre exemple de ce genre de comportement que j'ai pu trouver est l' ** opérateur, qui est un synonyme pour le plus commun opérateur exponentiel ^. Comme avec d'attribution de droits, ** ne semble pas être "reconnu" comme un appel de fonction, etc... par l'interprète:

R> `->`
#Error: object '->' not found
R> `**`
#Error: object '**' not found 

J'ai trouvé ce parce que c'est le seul autre cas où l' install_and_save2 est utilisé par le C de l'analyseur:

case '*':
  /* Replace ** by ^.  This has been here since 1998, but is
     undocumented (at least in the obvious places).  It is in
     the index of the Blue Book with a reference to p. 431, the
     help for 'Deprecated'.  S-PLUS 6.2 still allowed this, so
     presumably it was for compatibility with S. */
  if (nextchar('*')) {
    yylval = install_and_save2("^", "**");
    return '^';
  } else
    yylval = install_and_save("*");
return c;

2) Quand exactement est-ce possible? J'ai dans l'esprit que substitut(3 -> y) a déjà retournée à l'expression; je ne pouvais pas comprendre à partir de la source de ce substitut ne qui aurait ping la YACC...

Bien sûr, je suis toujours spéculer ici, mais oui, je pense que nous pouvons supposer que lorsque vous appelez substitute(3 -> y), du point de vue de la substitution de la fonction, l'expression a toujours été y <- 3; par exemple, la fonction est complètement inconscients que vous avez tapé 3 -> y. do_substitute,, comme 99% de la C fonctions utilisées par R, ne gère SEXP arguments - EXPRSXP dans le cas d' 3 -> y (== y <- 3- )), je crois. C'est ce à quoi je faisais allusion ci-dessus, quand j'ai fait une distinction entre le R de l'Environnement et de l'analyse de processus. Je ne pense pas qu'il y a quelque chose qui déclenche spécifiquement l'analyseur de printemps dans l'action, mais plutôt tout ce que vous avez entrés dans l'interprète obtient analysée. J'ai fait un peu plus de lecture sur la YACC / Bison analyseur générateur de la nuit dernière, et comme je le comprends (un.k.un. ne pas parier la ferme sur ce point), le Bison utilise la grammaire que vous définissez (en .y fichier(s)) pour générer un analyseur syntaxique en C - à-d. une fonction en C qui effectue l'analyse de l'entrée. À son tour, tout ce que vous avez saisie dans un R de session est d'abord traité par cette C fonction d'analyse syntaxique, qui ensuite les délégués les mesures à prendre dans le R de l'Environnement (j'utilise ce terme vaguement par la voie). Au cours de cette phase, lhs -> rhs obtiendrez traduit rhs <- lhs, ** de ^, etc... Par exemple, c'est un extrait de l'une des tables de fonctions primitives dans les noms.c:

/* Language Related Constructs */

/* Primitives */
{"if",      do_if,      0,  200,    -1, {PP_IF,      PREC_FN,     1}},
{"while",   do_while,   0,  100,    2,  {PP_WHILE,   PREC_FN,     0}},
{"for",     do_for,     0,  100,    3,  {PP_FOR,     PREC_FN,     0}},
{"repeat",  do_repeat,  0,  100,    1,  {PP_REPEAT,  PREC_FN,     0}},
{"break",   do_break, CTXT_BREAK,   0,  0,  {PP_BREAK,   PREC_FN,     0}},
{"next",    do_break, CTXT_NEXT,    0,  0,  {PP_NEXT,    PREC_FN,     0}},
{"return",  do_return,  0,  0,  -1, {PP_RETURN,  PREC_FN,     0}},
{"function",    do_function,    0,  0,  -1, {PP_FUNCTION,PREC_FN,     0}},
{"<-",      do_set,     1,  100,    -1, {PP_ASSIGN,  PREC_LEFT,   1}},
{"=",       do_set,     3,  100,    -1, {PP_ASSIGN,  PREC_EQ,     1}},
{"<<-",     do_set,     2,  100,    -1, {PP_ASSIGN2, PREC_LEFT,   1}},
{"{",       do_begin,   0,  200,    -1, {PP_CURLY,   PREC_FN,     0}},
{"(",       do_paren,   0,  1,  1,  {PP_PAREN,   PREC_FN,     0}},

Vous remarquerez qu' ->, ->>, et ** ne sont pas définis ici. Autant que je sache, R primitive des expressions telles que <- et [, etc... sont les plus proches de l'interaction de la R de l'Environnement a jamais avec tout C sous-jacente de code. Ce que je suggère, c'est que par cette étape dans le processus (de vous taper un ensemble de caractères dans l'interprète et la frappe de la touche 'Enter', jusqu'à la véritable évaluation valide de la R de l'expression), l'analyseur a déjà travaillé sa magie, qui est pourquoi vous ne pouvez pas obtenir une définition de fonction pour l' -> ou ** en les entourant avec des backticks, comme vous pouvez généralement.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X