当前位置:  开发笔记 > 前端 > 正文

R如何解析` - >`,右赋值运算符?

如何解决《R如何解析`->`,右赋值运算符?》经验,为你挑选了1个好方法。

所以这是一个微不足道的问题,但是我无法回答这个问题,也许答案会告诉我一些关于R如何工作的更多细节.

标题说明了一切:R如何解析->,模糊的右侧赋值函数?

我常用的技巧是失败:

`->`

错误:->找不到对象

getAnywhere("->")

没有->找到任何对象

我们不能直接称它为:

`->`(3,x)

错误:无法找到功能 "->"

但当然,它有效:

(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和直接`->`表示),那么它是什么?它完全属于自己的一类吗?

除了" ->R语言在解释和处理方式上是完全独特的,记忆并继续前进" 之外,还有什么需要从中学到的东西吗?



1> nrussell..:

让我先说一下,我对解析器的工作原理一无所知.话虽如此,gram.y的第296行定义了以下标记来表示(YACC?)解析器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);
}

因此,在使用解析器时没有经验,似乎->并且在解释过程中分别->>直接转换为<-非常低的级别.<<-


你提出了一个非常好的观点,询问解析器如何"知道"反转参数->- 考虑到它->似乎安装在R符号表中<-- 因此能够正确地解释x -> yy <- x 正确x <- y.我能做的最好的事情是提供进一步的推测,因为我继续遇到"证据"来支持我的说法.希望一些仁慈的YACC专家会偶然发现这个问题并提供一些见解; 不过,我不会屏住呼吸.

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

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

虽然我无法真正做出这种疯狂语法的正面或反面,但我确实注意到第二个和第三个参数xxbinary被交换为WRT LEFT_ASSIGN(xxbinary($2,$1,$3))和RIGHT_ASSIGN(xxbinary($2,$3,$1)).

这就是我脑海中的想象:

LEFT_ASSIGN 场景: y <- x

$2 是上述表达式中解析器的第二个"参数",即 <-

$1是第一个; 亦即y

$3 是第三个; x

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

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

$2被翻译-><-

$1x

$3y

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


进一步建立这个,我们xxbinary在gram.c的第3310行定义:

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

不幸的是我无法找到一个合适的定义lang3(或它的变体lang1,lang2等...),在R源代码,但是我假设它是用于在与同步的方式评估特殊功能(即符号)口译员.


更新 我将尝试在评论中解决您的一些其他问题,因为我可以给出(非常)有限的解析过程知识.

1)这真的是R中唯一表现得像这样的对象吗?(我记住了通过哈德利的书的约翰·钱伯斯报价:这显然是谎言外域 - 还有什么类似这样的"存在是一个对象,一切都发生的一个函数调用的一切."?

首先,我同意这不属于该领域.我相信钱伯斯的引用涉及R环境,即在这个低级解析阶段之后都会发生的过程.不过,我会在下面再说一点.无论如何,我能找到的这种行为的唯一另一个例子是**运算符,它是更常见的取幂运算符的同义词^.与正确的赋值一样,**似乎不被"识别"为函数调用等...由解释器:

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

我发现这是因为它install_and_save2 是C解析器使用的唯一其他情况:

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)已经翻了表达; 我无法从消息来源中找出那会替代YACC的替代品......

当然我还在这里猜测,但是,是的,我认为我们可以安全地假设当你打电话时substitute(3 -> y),从替代函数的角度来看,表达总是如此 y <- 3 ; 例如,该功能完全不知道您输入的内容3 -> y.do_substitute就像R使用的99%的C函数一样,只处理SEXP参数 - EXPRSXP3 -> y(== y <- 3)的情况下,我相信.当我在R环境和解析过程之间做出区分时,这正是我在上面提到的.我不认为这有什么特异触发解析器春季行动-而是一切您输入到解释被解析.我昨晚做了一些关于YACC/Bison解析器生成器的更多阅读,据我所知(也就是不打赌这个),Bison使用你定义的语法(在.y文件中)生成一个C中的解析器 - 即一个C函数,它对输入进行实际解析.反过来,在R会话一切你输入首先由该C解析功能,然后委托给在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原始表达式如<-[等......是R环境与任何底层C代码最接近的交互.我建议的是,通过这个阶段(从你输入一组字符到解释器并点击'Enter',直到实际评估一个有效的R表达式),解析器已经发挥了它的魔力,这就是为什么正如您通常所做的那样,您无法获得函数定义->**用反引号包围它们.


在此期间,我敢说这个答案值得一个`gram.y`?好的,我应该认真回去工作......
@nrussell不客气.`lang3`等.是内联函数,可以找到[这里,在`$ RHOME/src/include/Rinlinedfuns.h`中](https://github.com/wch/r-source/blob/trunk/src/include/Rinlinedfuns.h #L300-L319).在我看来,他们的角色就是将各个令牌和解析后的表达式拼凑成类似列表的语言对象,构建一个完全解析的输入表达式版本.
只是为了记录(以及作为一个完整的解析器新手)我会注意到它似乎[区别](http://tldp.org/HOWTO/Lex-YACC-HOWTO-6.html#ss6.1 )在一个令牌的类型(这里是'RIGHT_ASSIGN`)和它的值(这里是`<-`,由`install_and_save2`分配给`yylval`)之间.在我看来**类型**用于指导表达式的解析(将我们发送到读取`{$$ = xxbinary($ 2,$ 3,$ 1); setId($$,@ $);}的分支`),而它的**值**是通过`xxbinary`的第一个参数(即`$ 2`)传递的.
推荐阅读
mobiledu2402852357
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有