R如何正确赋值运算符来parsing` – >`?

所以这是一个微不足道的问题,但是我不能回答这个问题,也许答案会教给我一些关于R如何工作的细节。

标题说明了这一切:R如何parsing-> ,不明确的右侧分配函数?

我常用的技巧来探究这个失败:

 `->` 

错误:对象->未find

 getAnywhere("->") 

没有find名为->对象

我们不能直接调用它:

 `->`(3,x) 

错误:找不到function"->"

但当然,它的工作原理是:

 (3 -> x) #assigns the value 3 to the name x # [1] 3 

看来R知道如何简单地反驳这个论点,但是我认为上述方法肯定会破解这个案子:

 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) 

将此与常规赋值运算符进行比较:

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

?"->" ?assignOps和R语言定义都只是简单地提到它作为正确的赋值运算符。

但是显然有一些关于如何使用的东西是独一无二的。 这不是一个函数/运算符(因为调用getAnywhere并直接指向`->`似乎performance出来),那么它是什么呢? 它完全在一个自己的阶级?

除了“ ->在R语言中完全是独一无二的,它是如何解释和处理的,还有什么可以从中学习呢?

让我先说一句,我对parsing器的工作一无所知。 话虽如此, gram.y的第296行定义了下列标记来表示在(YACC?)parsing器中的赋值R使用:

 %token LEFT_ASSIGN EQ_ASSIGN RIGHT_ASSIGN LBB 

然后, 在gram.c的第5140到5150行 ,看起来就像是相应的C代码:

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

最后,从gram.c的第5044行开始, 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); } 

再一次,在parsing器的工作中没有任何经验,在解释过程中,似乎“ ->和“ ->>分别被直接翻译成<-<<-


你提出了一个非常好的问题,就是如何parsing器“知道”将参数反转为-> – 考虑到->似乎被安装到R符号表中作为<- – 并且因此能够正确地解释x -> yy <- x不是 x <- y 。 我可以做的最好的是提供进一步的猜测,因为我继续遇到“证据”来支持我的主张。 希望有一些慈悲的YACC专家会在这个问题上磕磕碰碰,提供一点见解, 不过,我不会屏住呼吸。

回到gram.y的第383和384行 ,这看起来像是一些与上述LEFT_ASSIGNRIGHT_ASSIGN符号相关的parsing逻辑:

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

虽然我不能真正地使这个疯狂的语法xxbinary ,我注意到xxbinary的第二个和第三个参数交换到WRT LEFT_ASSIGNxxbinary($2,$1,$3) )和RIGHT_ASSIGNxxbinary($2,$3,$1) )。

这就是我在脑海中描绘的东西:

LEFT_ASSIGNscheme: y <- x

  • $2是上述expression式中parsing器的第二个“参数”,即<-
  • 第一个是$1 ; 即y
  • 第三名是3 $3 ; x

因此,产生的(C?)调用将是xxbinary(<-, y, x)

把这个逻辑应用到RIGHT_ASSIGN (即x -> y ,结合我之前关于交换的猜想,

  • $2->转换为<-
  • $1x
  • $3y

但是由于结果是xxbinary($2,$3,$1)而不是xxbinary($2,$1,$3) ,结果仍然xxbinary(<-, y, x)


进一步build立起来,我们在xxbinary的第3310行定义了xxbinary

 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; } 

不幸的是,我无法在R源代码中findlang3 (或其变体lang1lang2等)的正确定义,但我假设它用于评估特殊函数(即符号)与解释器同步。


更新我会尝试解决您在评论中的一些额外问题,因为我可以给予(非常)有限的parsing过程知识。

1)这真的是R中的唯一对象,就像这样? (我记得约翰·钱伯斯(John Chambers)通过哈德利(Hadley)的着作引用:“所有存在的东西都是一个对象,所发生的一切都是一个函数调用。”这显然不在这个领域之外 – 还有其他的东西吗?

首先,我同意这不在这个领域。 我相信钱伯斯的引用关系到R环境,即在这个低级parsing阶段之后都发生的进程。 不过,我会在下面详细介绍一下。 无论如何,我能find的这种行为的另一个例子是**运算符,它是更常见的指数运算符^的同义词。 就像正确的分配一样, **似乎不被“识别”为函数调用,等等。

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

我发现这是因为这是C语法分析器使用 install_and_save2的唯一情况:

 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)这到底是什么时候发生的? 我记得替代品(3 – > y)已经翻转了这个expression; 我无法从源头上找出什么替代品可以打击YACC

当然,我还在猜测,但是,我认为我们可以放心地假设,当你从替代函数的angular度来看substitute(3 -> y) ,expression式总是 y <- 3 。 例如,函数完全不知道你input了3 -> ydo_substitute ,就像R所使用的C函数的99%,只处理SEXP参数 – 在3 -> y (== y <- 3 )的情况下是EXPRSXP ,我相信。 当我在R环境和parsing过程之间进行区分时,这就是我所指的。 我不认为有任何具体触发parsing器进入行动的东西 – 而是你input到解释器的所有东西都被parsing。 我昨天晚上对YACC / Bisonparsing器生成器进行了一些了解,据我了解(也就是说不要在农场上下注),Bison使用你定义的语法(在.y文件中)来用C语言生成一个parsing器 – 也就是一个C语言函数,它实际上parsing了input。 反过来,您在R会话中input的所有内容都将首先由此Cparsing函数处理,然后委托在R环境中采取适当的操作(我使用这个术语非常松散)。 在这个阶段, lhs -> rhs将被转换为rhs <- lhs**^等等。例如,这是来自names.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}}, 

你会注意到->->>**在这里没有定义。 据我所知,R原语expression式(如<-[等等)是R Environment与任何底层C代码的最接近的交互。 我所build议的是,在这个阶段(从input一组字符到解释器并敲入“Enter”,通过对一个有效的Rexpression式的实际评估),parsing器已经发挥了它的魔力,这就是为什么你可以通过用反引号把它们包围起来,来获得->或者**的函数定义,就像你通常所做的那样。