R- सही कार्य संचालक के रूप में R- को `->` कैसे बताता है?



yacc (1)

तो यह एक तुच्छ प्रश्न है, लेकिन यह मुझे परेशान कर रहा है कि मैं इसका जवाब नहीं दे सकता, और शायद यह जवाब मुझे कुछ और जानकारी देगा कि आर कैसे काम करता है।

शीर्षक यह सब कहता है: आर पार्स -> , अस्पष्ट राइट-साइड असाइनमेंट फ़ंक्शन कैसे करता है?

इस में गोता लगाने की मेरी सामान्य चाल विफल:

`->`

त्रुटि: ऑब्जेक्ट -> नहीं मिला

getAnywhere("->")

नाम की कोई वस्तु नहीं मिली -> मिली

और हम इसे सीधे नहीं कह सकते:

`->`(3,x)

त्रुटि: फ़ंक्शन नहीं मिल सका "->"

लेकिन निश्चित रूप से, यह काम करता है:

(3 -> x) #assigns the value 3 to the name x
# [1] 3

ऐसा प्रतीत होता है कि आर तर्कों को उलट देना जानता है, लेकिन मुझे लगा कि उपरोक्त दृष्टिकोण निश्चित रूप से इस मामले को तोड़ देगा:

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 , और आर लैंग्वेज डेफिनेशन सभी बस इसे सही असाइनमेंट ऑपरेटर के रूप में पारित करने का उल्लेख करते हैं।

लेकिन स्पष्ट रूप से कुछ अनूठा है कि कैसे -> का उपयोग किया जाता है। यह एक फ़ंक्शन / ऑपरेटर नहीं है (जैसा कि getAnywhere और सीधे `->` को प्रदर्शित करने के लिए कॉल के रूप में), तो यह क्या है? क्या यह पूरी तरह से अपने स्वयं के वर्ग में है?

क्या इसके अलावा इससे सीखने के लिए कुछ भी है " -> आर भाषा के भीतर पूरी तरह से अद्वितीय है कि यह कैसे व्याख्या और संभाला जाता है? याद रखें और आगे बढ़ें"?


मुझे यह कहकर प्रस्तावना दें कि मैं जानता हूं कि पार्सर कैसे काम करता है। कहा जा रहा है कि, लाइन 296 gram.y में (YACC?) पार्सर आर उपयोग का प्रतिनिधित्व करने के लिए निम्नलिखित टोकन को परिभाषित करता है:

%token      LEFT_ASSIGN EQ_ASSIGN RIGHT_ASSIGN LBB

फिर, 5150 ग्राम रेखा के माध्यम से 5140 की तर्ज पर , यह इसी सी कोड की तरह दिखता है:

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

तो फिर से, पार्सर्स के साथ काम करने का शून्य अनुभव होने के बाद, ऐसा लगता है कि -> और ->> का अनुवाद सीधे <- और <<- , क्रमशः व्याख्या प्रक्रिया में बहुत निचले स्तर पर।

आपने यह पूछने में एक बहुत अच्छा बिंदु निकाला कि कैसे पार्सर "जानता है" -> तर्कों को उलटने के लिए -> - इस पर विचार करते हुए -> आर प्रतीक तालिका में <- - के रूप में स्थापित किया गया प्रतीत होता है और इस प्रकार x -> y सही ढंग से व्याख्या करने में सक्षम है। x -> y रूप में y <- x और x <- y । मैं जो सबसे अच्छा कर सकता हूं वह आगे की अटकलें प्रदान करता है क्योंकि मैं अपने दावों का समर्थन करने के लिए "सबूत" पर आना जारी रखता हूं। उम्मीद है कि कुछ दयालु YACC विशेषज्ञ इस प्रश्न पर ठोकर खाएंगे और थोड़ी अंतर्दृष्टि प्रदान करेंगे; मैं उस पर अपनी सांस नहीं जा रहा हूँ, हालांकि।

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 के दूसरे और तीसरे तर्क WRT LEFT_ASSIGN ( xxbinary($2,$1,$3) ) और RIGHT_ASSIGN ( xxbinary($2,$3,$1) xxbinary($2,$1,$3) ) को RIGHT_ASSIGN xxbinary($2,$3,$1) )।

यहाँ मैं अपने सिर में चित्र लगा रहा हूँ:

LEFT_ASSIGN परिदृश्य: y <- x

  • $2 उपरोक्त अभिव्यक्ति में पार्सर के लिए दूसरा "तर्क" है, अर्थात <-
  • $1 पहला है; अर्थात् y
  • $3 तीसरा है; x

इसलिए, परिणामी (C?) कॉल xxbinary(<-, y, x) होगा xxbinary(<-, y, x)

इस तर्क को RIGHT_ASSIGN , यानी x -> y लागू करते RIGHT_ASSIGN , मेरे पहले अनुमान के साथ RIGHT_ASSIGN <- और -> स्वैप किया गया,

  • $2 अनुवाद -> से <- हो जाता है
  • $1 x
  • $3 y

लेकिन चूंकि परिणाम xxbinary($2,$3,$1) बजाय xxbinary($2,$1,$3) , परिणाम अभी भी xxbinary(<-, y, x)

इससे थोड़ा आगे बढ़ते हुए, हमारे पास xxbinary की परिभाषा xxbinary लाइन 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 , आदि ...) की उचित परिभाषा नहीं मिल lang3 , लेकिन मैं यह मान रहा हूं कि इसका उपयोग विशेष कार्यों (यानी प्रतीकों) के मूल्यांकन के लिए किया जाता है। दुभाषिया के साथ सिंक्रनाइज़ है।

अपडेट मैं आपके कुछ अतिरिक्त प्रश्नों को टिप्पणियों में संबोधित करने की कोशिश करूंगा क्योंकि मैं अपनी (बहुत) पार्सिंग प्रक्रिया का सीमित ज्ञान दे सकता हूं।

1) यह वास्तव में आर में एकमात्र वस्तु है जो इस तरह का व्यवहार करता है ?? (हेडली की पुस्तक के माध्यम से जॉन चैंबर्स के उद्धरण को ध्यान में रखते हुए मैंने कहा: "जो कुछ भी मौजूद है वह एक वस्तु है। एक फ़ंक्शन कॉल होने वाली हर चीज़।" यह स्पष्ट रूप से उस डोमेन के बाहर स्थित है - क्या ऐसा कुछ और है?

पहले, मैं मानता हूं कि यह उस डोमेन के बाहर है। मेरा मानना ​​है कि चेम्बर्स की बोली आर एनवायरनमेंट की चिंता करती है, यानी इस निम्न स्तर के पार्सिंग चरण के बाद होने वाली प्रक्रियाएँ। मैं इस पर थोड़ा और नीचे का स्पर्श करूँगा। वैसे भी, इस तरह के व्यवहार का केवल दूसरा उदाहरण मुझे मिल सकता है ** ऑपरेटर, जो अधिक सामान्य घातांक ऑपरेटर ^ लिए एक पर्याय है। सही असाइनमेंट के साथ, ** किसी फ़ंक्शन कॉल आदि के रूप में "पहचाने हुए" नहीं लगते ... दुभाषिया द्वारा:

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 द्वारा उपयोग किए गए C फ़ंक्शन के 99% की तरह, केवल 3 -> y (= = y <- 3 ) के मामले में SEXP तर्क - एक EXPRSXP को संभालता है, मेरा मानना ​​है। यह वही है जो मैं आर पर्यावरण और पार्सिंग प्रक्रिया के बीच अंतर करने के लिए ऊपर से बता रहा था। मुझे नहीं लगता कि ऐसा कुछ भी है जो विशेष रूप से वसंत के लिए पार्सर को कार्रवाई में ट्रिगर करता है - लेकिन इसके बजाय आप जो कुछ भी दुभाषिया में इनपुट करते हैं वह पार्स हो जाता है। मैंने कल रात YACC / बाइसन पार्सर जनरेटर के बारे में थोड़ा और अधिक पढ़ा था, और जैसा कि मैं समझता हूं (उर्फ इस पर खेत को दांव नहीं लगाता), बाइसन आपके द्वारा परिभाषित व्याकरण ( .y फ़ाइल (ओं) में) का उपयोग करता है। C में पार्सर उत्पन्न करें - अर्थात C फ़ंक्शन जो इनपुट के वास्तविक पार्सिंग को करता है। बदले में, आर सत्र में आप जो कुछ भी इनपुट करते हैं, उसे पहले इस सी पार्सिंग फ़ंक्शन द्वारा संसाधित किया जाता है, जो तब आर एनवायरमेंट में लिए जाने वाले उचित एक्शन को दर्शाता है (मैं इस शब्द का उपयोग बहुत शिथिल तरीके से कर रहा हूं)। इस चरण के दौरान, lhs -> rhs का अनुवाद rhs <- lhs , ** से ^ , इत्यादि में किया जाएगा ... उदाहरण के लिए, यह एक व्यापक रूप से प्रचलित है

/* 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}},

आप देखेंगे कि -> , ->> , और ** यहाँ परिभाषित नहीं हैं। जहाँ तक मुझे पता है, आर आदिम अभिव्यक्तियाँ जैसे <- और [ , आदि ... निकटतम अंतःक्रिया आर एनवायरनमेंट में कभी भी किसी अंतर्निहित कोड के साथ होती है। मैं जो सुझाव दे रहा हूं, वह यह है कि इस चरण की प्रक्रिया में (दुभाषिया में एक सेट वर्ण टाइप करने से और 'एंटर' मारने से, एक वैध आर अभिव्यक्ति के वास्तविक मूल्यांकन के माध्यम से), पार्सर पहले ही अपना जादू चला चुका है, यही वजह है कि आप आम तौर पर कर सकते हैं के रूप में -> या ** backticks के साथ उन्हें आसपास से एक परिभाषा परिभाषा नहीं मिल सकता है।





yacc