[解決済み] Rは右代入演算子 `->` をどのように解析しているのですか?
質問
これは些細な質問なのですが、答えられないことが気になります。そしておそらくその答えは、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言語の中で完全にユニークであることです。
どのように解決するのですか?
前置きが長くなりましたが、私はパーサがどのように動作するかについてまったく何も知りません。とは言うものの の 296 行目、gram.y は、Rが使う(YACC?)パーサで代入を表すために以下のトークンを定義しています。
%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;
}
}
最後に
の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( $$, @$); }
このおかしな構文を理解することはできませんが、2番目と3番目の引数が
xxbinary
の第二引数と第三引数が入れ替わり、WRT
LEFT_ASSIGN
(
xxbinary($2,$1,$3)
) と
RIGHT_ASSIGN
(
xxbinary($2,$3,$1)
).
頭の中でイメージしているのは、こんな感じです。
LEFT_ASSIGN
シナリオ
y <- x
-
$2
は上記の式のパーサーへの第二引数"です、つまり<-
-
$1
は最初のもので、すなわちy
-
$3
は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行目
:
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
, etc...)をRのソースコードに埋め込んでいますが、インタープリタと同期した形で特殊な関数(つまりシンボル)を評価するために使われているのではと推測しています。
更新情報 私は構文解析プロセスに関する私の(非常に)限られた知識を考慮して、コメントであなたの追加の質問のいくつかにできる限り対処しようと思います。
1) このような動作をするRのオブジェクトは本当にこれだけなのでしょうか?(私は ハドレー氏の本から引用したジョン・チェンバースの言葉が頭にあります。 存在するものはすべてオブジェクトである。存在するものはすべてオブジェクトであり、起こることはすべて関数呼び出しである(")。 これは明らかにその領域の外側にあります。 このようなものは他にあるでしょうか?
まず、これがその領域の外側にあることに同意します。Chambers の引用は、R 環境、つまり、この低レベルの解析段階の後に行われるすべての処理に関するものだと思います。しかし、この点については、以下でもう少し触れます。とにかく、この種の動作の他の例として私が見つけたのは
**
演算子です。これはより一般的な指数演算子の同義語である
^
. 右代入と同様に
**
はインタープリタによって関数呼び出しなどとして "quot;認識されないようです。
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) 具体的にどのような場合にそうなるのでしょうか?私は、substitute(3 -> y)はすでに式を反転させています。ソースからは、YACCにピンを刺すようなsubstituteの動作がわかりませんでした...。
もちろん、私はまだここで推測しているのですが、はい、私は、あなたが
substitute(3 -> y)
から見て
の観点からは、代入関数
という式は
は常に
y <- 3
例えば、この関数はあなたが
3 -> y
.
do_substitute
は、Rで使用される99%のC関数と同様に、唯一の処理である
SEXP
引数を扱います。
EXPRSXP
の場合
3 -> y
(==
y <- 3
) と思っています。これは、私が上記でR環境と構文解析プロセスを区別したときに言及したことです。特にパーサーが動き出すきっかけとなるものはないと思います - むしろ
すべて
を入力するとパースされます。私は
少し
YACC / Bison パーサーについてさらに読みました。
ジェネレータ
昨夜、私が理解したところでは (つまり、これに賭けないでください)、Bison はあなたが定義した文法 (
.y
ファイルで)定義した文法を使用して
を生成します。
を生成します。つまり、入力の実際のパージングを行うC関数です。つまり、R セッションで入力されたものはすべて、まずこの C のパーシング関数によって処理され、次に R Environment (この用語は非常に大雑把に使っています) で実行される適切なアクションが委譲されるのです。このフェーズでは
lhs -> rhs
は次のように翻訳されます。
rhs <- lhs
,
**
から
^
などなど...。例えば、これはある1つの
の表から抜粋したものです。
:
/* 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式の実際の評価まで)では、パーサーはすでに魔法をかけており、そのため、関数定義を得ることができない、ということです。
->
または
**
のように、バックティックで囲んでください。
関連
-
[解決済み] 三項演算子はRに存在するか?
-
[解決済み] データフレームを結合(マージ)する方法(内側、外側、左側、右側)
-
[解決済み] Rの代入演算子"="と"<-"の違いは何ですか?
-
[解決済み] 関数のソースコードを見るにはどうしたらいいですか?
-
[解決済み] 空のdata.frameを作成する
-
[解決済み] 統計的最頻値の求め方は?
-
[解決済み】data.table vs dplyr:一方がうまくできない、またはうまくできないことを行うことができますか?
-
[解決済み】data.frameのグループごとの平均値【重複】について
-
[解決済み】エラー:Rで関数が見つかりませんでした。
-
[解決済み] セッションが作成されません。このバージョンのChromeDriverはChromeバージョンにしか対応していません ChromeDriver ChromeでSeleniumを使用した場合の74エラー
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
R: hclust(d, method = method)でのエラー : 外部関数呼び出しは NA/NaN/Inf(arg10) を持つことができません。
-
Rの警告 "条件の長さが1より大きいので、最初の要素しか使えない "に対する解決策
-
R言語 - マトリックス
-
[解決済み] lm.fit(x,y,offset = offset, singular.ok,...) 0 非NAケースでboxcox式で計算するとエラーになる。
-
[解決済み] Rで2つのグラフを同じプロットで表示する
-
[解決済み] コマンドラインからRスクリプトを実行する
-
[解決済み] ベクトル中のxの値を持つ要素の個数を数える
-
[解決済み] R および RStudio のコンソールをクリアする関数
-
[解決済み】RでCRANミラーを選択する方法
-
[解決済み] 2つの単語の最初の文字を大文字にします。