所以这是一个很小的问题,但我不能回答这个问题,这让我感到困扰,也许这个答案会教我更多关于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

?"->"?assignOpsR Language Definition都只是顺便提到它是正确的赋值运算符.

->的使用方式显然有其独特之处.它不是一个函数/运算符(正如对getAnywhere和直接对`->`的调用所显示的那样),那么它是什么呢?它完全属于自己的一个类吗?

除了"->在R语言中的解释和处理方式是完全独特的;记住并继续"之外,还有什么值得学习的吗?

推荐答案

让我先说一句,我对解析器的工作原理一无所知.话虽如此,line 296 of gram.y定义了以下令牌来表示(YACC?)中的赋值解析器R使用:

%token      LEFT_ASSIGN EQ_ASSIGN RIGHT_ASSIGN LBB

然后,on lines 5140 through 5150 of gram.c,这看起来像对应的C代码:

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

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

同样地,由于没有使用解析器的经验,在解释过程中,->->>似乎分别在very low level处被直接翻译成<-<<-.


您提出了一个非常好的问题,即解析器如何"知道"将参数反转为->(考虑到->似乎作为<-安装到R符号表中),从而能够正确地将x -> y解释为y <- xnot x <- y.我所能做的就是提供进一步的推测,因为我继续遇到支持我的主张的"证据".希望一些仁慈的YACC专家会偶然发现这个问题,并提供一些见解;不过,我不会为此屏住呼吸.

回到lines 383 and 384 of gram.y,这看起来像是与前面提到的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->转为<-
  • $1等于x
  • $3等于y

但由于结果是xxbinary($2,$3,$1)而不是xxbinary($2,$1,$3),因此结果是still xxbinary(<-, y, x).


在此基础上,我们得到了xxbinaryline 3310 of gram.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;
}

不幸的是,我找不到lang3(或其变体lang1lang2等)的正确定义在R源代码中,但我假设它用于以与解释器同步的方式计算特殊函数(即符号).


Updates

1) 这真的是R中唯一一个这样的对象吗??(我已经

首先,我同意这不属于该领域.我相信钱伯斯的话与R环境有关,也就是说,所有的过程都发生在这个低级解析阶段之后.不过,我将在下面进一步讨论这个问题.无论如何,我能找到的这种行为的另一个例子是**运算符,它是更常见的求幂运算符^的同义词.与right赋值一样,**似乎不被"识别"为函数调用,等等...翻译:

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

我发现这个是因为这是唯一一个install_and_save2 is used by the C parser:

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) When exactly does this happen? I've got in mind that substitute(3 -> y) has already flipped the expression; I couldn't figure out from the source what substitute does that would have pinged the YACC...

当然我还在猜测,但是是的,我认为我们可以安全地假设,当你打substitute(3 -> y),从the substitute function的Angular 来看,表达always was y <- 3;e、 g.函数完全不知道您键入了3 -> y.do_substitute,就像R使用的99%的C函数一样,只处理SEXP个参数——我相信在3 -> y(=y <- 3)的情况下是EXPRSXP个参数.这就是我在上面对R环境和解析过程进行区分时提到的.我不认为有任何东西会特别触发解析器启动,而是让您输入到解释器中的内容得到解析.昨晚我又读了little篇关于YACC/Bison解析器generator的文章,据我所知(也就是说,不要把赌注押在这个问题上),Bison使用你定义的语法(在.y个文件中)来解析C中的解析器,即C函数,它实际解析输入.反过来,在R会话中输入的所有内容都首先由这个C解析函数处理,然后它会委托在R环境中采取适当的操作(顺便说一句,我使用这个术语非常松散).在这个阶段,lhs -> rhs将被转换为rhs <- lhs**^,等等...例如,本文摘自tables of primitive functions in 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代码之间最密切的交互.我想说的是,在这个过程中(从你在解释器中输入一组字符,然后点击"回车",直到对一个有效的R表达式进行实际计算),解析器已经发挥了它的魔力,这就是为什么你不能通过在->**中加上反勾来获得函数定义,就像你通常能做的那样.

R相关问答推荐

根据特定列中的数据框条目添加新列的最快方法是什么

如何对所有 NA 为 0/NA 的行求和

在 data.table `by` 中使用 `fifelse`,其中测试列是 `by` 的一部分

如何根据先前的正方形递归创建正方形?

反复计算

如何计算每个月和每年的总数?

在R中按组查找最长的值序列

sf 数据框中几何或坐标的“地板”空间类似功能

按其他日期列中的信息填充缺失的变量 (R)

如何计算R中每一行的每一列的周期总和

调用堆栈的稳定性如何,特别是 -1?

将一行转换成combine,c()作为r中的向量,然后用向量计算余弦相似度

在 group_by 中,改变一个新列,该列根据列的出现顺序获取列的值

如何使用 cross 和 .names 参数将此基本 R 代码转换为 dplyr

在 R 中推断顺序数据中的选择顺序

使用 R 在 map() 中更新循环外的向量

R:重新排列数据框中行的顺序

如何在 R 中设置错误消息的宽度?

R:如何在一个图上绘制多个图(shiny )

在函数定义期间判断并保存参数变量值?