_(アンダースコア)変数はどこでどのように指定されますか?


81

ほとんどの人は_、最後の戻り値のホルダーとしてのIRBでの特別な意味を認識していますが、それは私がここで求めていることではありません。

代わりに、_plain-old-Ruby-codeで変数名として使用される場合について質問しています。ここでは、「ドントケア変数」(àlaProlog)に似た特別な動作があるように見えます。以下に、その固有の動作を示すいくつかの便利な例を示します。

lambda { |x, x| 42 }            # SyntaxError: duplicated argument name
lambda { |_, _| 42 }.call(4, 2) # => 42
lambda { |_, _| 42 }.call(_, _) # NameError: undefined local variable or method `_'
lambda { |_| _ + 1 }.call(42)   # => 43
lambda { |_, _| _ }.call(4, 2)  # 1.8.7: => 2
                                # 1.9.3: => 4
_ = 42
_ * 100         # => 4200
_, _ = 4, 2; _  # => 2

これらはすべてputs、追加機能との競合を避けるために、IRBではなくRubyで直接(sが追加されて)実行されました。

ただし、これはすべて私自身の実験の結果です。この動作に関するドキュメントはどこにも見つからないためです(確かに、検索するのが最も簡単な方法ではありません)。最終的に、これらすべてが内部でどのように機能するのか興味があり、何が特別なのかを正確に理解することができます_。そのため、ドキュメントへの参照、できれば、Rubyでの動作を明らかにするRubyソースコード(およびおそらくRubySpec)への参照を求めています_。

注:これのほとんどは、@ NiklasBとのこの話し合いから生じました。

回答:


54

「引数名の重複」エラーを抑制するために、ソースには特別な処理がいくつかあります。エラーメッセージはshadowing_lvar_gen内部parse.yにのみ表示され、1.9.3バージョンは次のようになります。

static ID
shadowing_lvar_gen(struct parser_params *parser, ID name)
{
    if (idUScore == name) return name;
    /* ... */

そして、idUScoreされる中で定義されid.c、このように:

REGISTER_SYMID(idUScore, "_");

同様の特別な処理がwarn_unused_var:に表示されます。

static void
warn_unused_var(struct parser_params *parser, struct local_vars *local)
{
    /* ... */
    for (i = 0; i < cnt; ++i) {
        if (!v[i] || (u[i] & LVAR_USED)) continue;
        if (idUScore == v[i]) continue;
        rb_compile_warn(ruby_sourcefile, (int)u[i], "assigned but unused variable - %s", rb_id2name(v[i]));
    }
}

forループの2行目で警告が抑制されていることがわかります。

_1.9.3ソースで見つけた唯一の特別な処理は上記のとおりです。名前の重複エラーが抑制され、未使用の変数の警告が抑制されます。これらの2つを除け_ば、他の変数と同じように単純な古い変数です。の(マイナーな)特殊性に関するドキュメントを知りません_。

Ruby 2.0では、idUScore == v[i]テストインwarn_unused_varは次の呼び出しに置き換えられますis_private_local_id。

if (is_private_local_id(v[i])) continue;
rb_warn4S(ruby_sourcefile, (int)u[i], "assigned but unused variable - %s", rb_id2name(v[i]));

とis_private_local_idで始まる変数に対する警告を抑制_:

if (name == idUScore) return 1;
/* ... */
return RSTRING_PTR(s)[0] == '_';

_それ自体ではなく。したがって、2.0は物事を少し緩めます。


1
lambda { |_, _| _ }.call(4, 2)1.8と1.9の動作の違いは、意図しない副作用にすぎないのでしょうか。変数名を複製できない「通常の」状況では、割り当てられた順序は重要ではありません。
— Andrew Marshall

3
@AndrewMarshall:はい、「4 vs 2」の問題は、1.8と1.9がスタックを処理する方法のアーティファクトにすぎないと思います。目立つのは|_,_,...|、重複エラーが抑制されているためだけです。
— muは短すぎる

2
@AndrewMarshall:誰もが私たちの後ろでお互いのChangeLogsを読んでいるのだろうか。
— muは短すぎる

2
素敵な発見。二重パラメータ名のエラーを抑えるだけで、そのような単純なものになると思いました。Rubyは本当に大きな混乱です:D
— Niklas B.

2
@mu:もちろん、インタープリター型言語の本当にクリーンな実装はまだ見ていません(Luaが近づいています)。
— Niklas B.

24

_は有効な識別子です。識別子にアンダースコアを含めるだけでなく、アンダースコアにすることもできます。

_ = o = Object.new
_.object_id == o.object_id
# => true

メソッド名として使用することもできます。

def o._; :_ end
o._
# => :_

もちろん、これは正確に読み取り可能な名前ではなく、変数が何を参照しているのか、メソッドが何をしているのかについての情報を読者に渡すものでもありません。

IRB特に、_最後の式の値に設定します。

$ irb
> 'asd'
# => "asd"
> _
# => "asd"

そのままソースコードに、それは単にセット_の最後の値に:

@workspace.evaluate self, "_ = IRB.CurrentContext.last_value"

いくつかのリポジトリを探索しました。これが私が見つけたものです:

ファイルの最後の行に、次id.cの呼び出しがあります。

REGISTER_SYMID(idUScore, "_");

grepソースをidUScore検索すると、一見関連性のある2つの結果が得られました。

shadowing_lvar_genブロックの仮パラメータが、別のスコープに存在する同じ名前の変数を置き換えるメカニズムのようです。「引数名の重複」SyntaxErrorと「外部ローカル変数のシャドウイング」の警告が発生するように見える関数です。

grepのソースを取得した後、Ruby1.9.3の変更ログでshadowing_lvar_gen次のことがわかりました。

2007年12月11日火曜日01:21:21まつもとゆきひろ

  • parse.y(shadowing_lvar_gen):「_」の重複エラーはありません。

これがこの行の起源である可能性があります:

if (idUScore == name) return name;

このことから、のような状況ではproc { |_, _| :x }.call :a, :b、一方の_変数がもう一方の変数を単純にシャドウイングしていると推測します。


これが問題のコミットです。それは基本的にこれらの2つの行を導入しました:

if (!uscore) uscore = rb_intern("_");
if (uscore == name) return;

idUScore存在すらしなかった時代から、どうやら。


6
これは、なぜlambda { |_, _| 42 }機能するのにlambda { |x, x| 42 }機能しないのかをまったく説明していません。
— Andrew Marshall

@AndrewMarshall、あなたは正しいようです。|_, _|動作し|__, __|ますが、動作しません。_特別な意味があるようですが、Rubyソースから情報を掘り下げることができるかどうかを確認します。
— Matheus Moreira 2012年

1
ちなみに、あなたの更新は有益ですが、私が尋ねていなかった私の質問で具体的に述べたIRbにのみ適用されるため、関連性がありません。
— Andrew Marshall

1
ChangeLogエントリを掘り起こすための+1。誰もがCハッカーでなければなりません:)
— muは短すぎます

1
IRBソースコードの場合は+1。IRB.CurrentContext.last_valueは、提起された質問に関連していなくても、知っておくと非常に興味深いものです。私はIRBアンダースコアについてのグーグル検索からここに行き着きました。
— Josh
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.