所以这是一个微不足道的问题,但是我无法回答这个问题,也许答案会告诉我一些关于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 Language Definition都只是简单地提及它作为正确的赋值运算符。
但显然有一些关于如何使用->
的独特之处。它不是函数/运算符(因为调用getAnywhere
并且直接调用`->`
似乎证明了),那么它是什么?它完全属于自己的一类吗?
除了“->
在R语言中如何解释和处理它之外,还有什么值得学习的东西;记住并继续前进”?
答案 0 :(得分:71)
让我先说一下,我完全不知道解析器是如何工作的。话虽如此,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);
}
再次,由于没有使用解析器的经验,似乎->
和->>
分别直接转换为<-
和<<-
解释过程中的水平很低。
你提出了一个非常好的观点,询问解析器&#34;如何知道&#34;将->
的参数反转 - 考虑->
似乎作为<-
安装到R符号表中 - 从而能够正确地将x -> y
解释为y <- x
}和不 x <- y
。我能做的最好的事情是提供进一步的推测,因为我继续遇到&#34;证据&#34;支持我的主张。希望一些仁慈的YACC专家会偶然发现这个问题并提供一些见解;不过,我不会屏住呼吸。
返回lines 383 and 384 of gram.y,这看起来像是与上述LEFT_ASSIGN
和RIGHT_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
是第二个&#34;参数&#34;上述表达式中的解析器,即<-
$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)
,因此仍 xxbinary(<-, y, x)
。
进一步加强这一点,我们在line 3310 of gram.c上有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源代码中找不到lang3
(或其变体lang1
,lang2
等等)的正确定义,但我还是假设它用于以与解释器同步的方式评估特殊函数(即符号)。
<强>更新强> 我将尝试在评论中解决您的一些其他问题,因为我可以在解析过程中获得(非常)有限的知识。
1)这真的是R中唯一表现得像这样的对象吗? (I&#39;已经 记住约翰钱伯斯引用哈德利的书:&#34;一切 存在是一个对象。发生的一切都是函数调用。&#34; 这明显位于该领域之外 - 还有其他类似的东西 此?
首先,我同意这不属于该领域。我相信钱伯斯&#39;引用涉及R环境,即在此低级解析阶段之后全部发生的过程。不过,我会在下面再说一点。无论如何,我能找到的这种行为的唯一另一个例子是**
运算符,它是更常见的取幂运算符^
的同义词。与正确的作业一样,**
似乎并未得到认可&#34;作为函数调用等...由解释器:
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)究竟是什么时候发生的?我已经记住了替代品(3 - &GT; y)已经翻了表达;我无法从消息来源中找出那会替代YACC的替代品......
我当然还在这里推测,但是,我认为我们可以安全地假设当你从the substitute function的角度来电话substitute(3 -> y)
时,表达式始终是 y <- 3
;例如该函数完全不知道您键入了3 -> y
。与{R}使用的99%的C函数一样,do_substitute
仅处理SEXP
个参数 - EXPRSXP
(== 3 -> y
时的y <- 3
, 我相信。当我在R环境和解析过程之间做出区分时,这正是我在上面提到的。我不认为有什么能够特别触发解析器开始行动 - 而是输入解释器的一切得到解析。我做了一个关于YACC / Bison解析器发生器的小更多的阅读,据我所知(也就是说不要打赌农场),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代码最接近的交互。我建议的是,通过这个阶段(从你输入一组字符到解释器并点击&#39; Enter&#39;,通过对有效R表达式的实际评估),解析器已经工作了魔术,这就是为什么你不能像往常一样用反引号包围它们来获得->
或**
的函数定义的原因。