[英]How exactly does R parse `->`, the right-assignment operator?
所以這是一個微不足道的問題,但是我無法回答這個問題,也許答案會告訴我一些關於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語言中它是如何被解釋和處理的完全獨特之外,還有什么需要從中學到的東西;記住並繼續前進”?
讓我先說一下,我對解析器的工作原理一無所知。 話雖如此, 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 -> y
為y <- x
而不是 x <- y
。 我能做的最好的事情是提供進一步的推測,因為我繼續遇到“證據”來支持我的說法。 希望一些仁慈的YACC專家會偶然發現這個問題並提供一些見解; 不過,我不會屏住呼吸。
回到gram.y的第383行和第384行 ,這看起來像是與上述LEFT_ASSIGN
和RIGHT_ASSIGN
符號相關的更多解析邏輯:
| expr LEFT_ASSIGN expr { $$ = xxbinary($2,$1,$3); setId( $$, @$); }
| expr RIGHT_ASSIGN expr { $$ = xxbinary($2,$3,$1); setId( $$, @$); }
雖然我無法真正做出這種瘋狂語法的xxbinary
,但我確實注意到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)
,結果仍然是xxbinary(<-, y, x)
。
進一步建立這個,我們在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源代碼中找不到lang3
(或其變體lang1
, lang2
等等)的正確定義,但我假設它用於以某種方式評估特殊函數(即符號)與解釋器同步。
更新我將嘗試在評論中解決您的一些其他問題,因為我可以給出(非常)有限的解析過程知識。
1)這真的是R中唯一表現得像這樣的對象嗎? (我記得約翰·錢伯斯引用哈德利的書:“存在的一切都是一個對象。所發生的一切都是一個函數調用。”這顯然屬於這個領域之外 - 還有其他類似的東西嗎?
首先,我同意這不屬於該領域。 我相信錢伯斯的引用涉及R環境,即在這個低級解析階段之后都會發生的過程。 不過,我會在下面再說一點。 無論如何,我能找到的這種行為的唯一另一個例子是**
運算符,它是更常見的取冪運算符^
的同義詞。 與正確的賦值一樣, **
似乎不被“識別”為函數調用等...由解釋器:
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)已經翻了表達; 我無法從消息來源中找出那會替代YACC的替代品......
當然我還在這里推測,但是,我認為我們可以安全地假設當你調用substitute(3 -> y)
,從替代函數的角度來看,表達式總是 y <- 3
; 例如,該函數完全不知道您鍵入3 -> y
。 do_substitute
,就像R使用的99%的C函數一樣,只處理SEXP
參數 - 在3 -> y
(== y <- 3
)的情況下是EXPRSXP
,我相信。 當我在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表達式),解析器已經發揮了它的魔力,這就是為什么你通常可以通過用反引號包圍它們來獲得->
或**
的函數定義。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.