簡體   English   中英

R如何解析` - >`,右賦值運算符?

[英]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

?"->" ?assignOpsR語言定義都只是簡單地提及它作為正確的賦值運算符。

但是顯然有一些關於如何使用->獨特之處。 它不是一個函數/運算符(因為調用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 -> 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 ,但我確實注意到xxbinary的第二個和第三個參數被交換為WRT LEFT_ASSIGNxxbinary($2,$1,$3) )和RIGHT_ASSIGNxxbinary($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第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 (或其變體lang1lang2等等)的正確定義,但我假設它用於以某種方式評估特殊函數(即符號)與解釋器同步。


更新我將嘗試在評論中解決您的一些其他問題,因為我可以給出(非常)有限的解析過程知識。

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM