指定_(下划线)变量的位置和方式是什么?

And*_*all 76 ruby

大多数人都知道_IRB作为最后回报值的持有人的特殊含义,但这不是我在这里要求的.

相反,我问的是什么_时候用作普通旧Ruby代码中的变量名.在这里它似乎有特殊的行为,类似于"不关心变量"(à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
Run Code Online (Sandbox Code Playgroud)

这些都直接在Ruby中运行(puts添加了s) - 不是IRB-以避免与其附加功能冲突.

这完全是我自己的实验的结果,因为我无法在任何地方找到关于此行为的任何文档(不可否认,这不是最容易搜索的事情).最后,我很好奇所有这些内部是如何工作的,所以我可以更好地理解究竟什么是特别的_.所以我要求提供文档,最好是Ruby源代码(也许是RubySpec),它们揭示了_Ruby中的行为.

注意:大部分内容都来自与@Niklas B的讨论.

mu *_*ort 52

源中有一些特殊处理可以抑制"重复参数名称"错误.错误消息只出现在shadowing_lvar_gen内部parse.y,1.9.3版本如下所示:

static ID
shadowing_lvar_gen(struct parser_params *parser, ID name)
{
    if (idUScore == name) return name;
    /* ... */
Run Code Online (Sandbox Code Playgroud)

并且idUScore在定义id.c这样的:

REGISTER_SYMID(idUScore, "_");
Run Code Online (Sandbox Code Playgroud)

你会看到类似的特殊处理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]));
    }
}
Run Code Online (Sandbox Code Playgroud)

您会注意到警告在for循环的第二行被禁止.

_我在1.9.3源代码中可以找到的唯一特殊处理是:禁止重复名称错误,并禁止使用未使用的变量警告.除了这两件事之外,_它只是一个普通的旧变量.我不知道有关(次要)特殊性的任何文件_.

在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]));
Run Code Online (Sandbox Code Playgroud)

is_private_local_id禁止以下列开头的变量发出警告_:

if (name == idUScore) return 1;
/* ... */
return RSTRING_PTR(s)[0] == '_';
Run Code Online (Sandbox Code Playgroud)

而不仅仅是_自己.所以2.0放松了一些东西.

  • @AndrewMarshall:是的,我认为"4 vs 2"问题只是1.8和1.9如何处理堆栈的工件.唯一值得注意的是`| _,_,... |`,因为重复错误已被抑制. (3认同)
  • @AndrewMarshall:我想知道是否所有人都在阅读我们背后的对方的ChangeLogs. (2认同)
  • 很好找.我假设它会像这样简单,只是抑制双参数名称错误.Ruby确实是一个巨大的混乱:D (2认同)
  • @mu:当然,我还没有看到一个非常干净的解释语言实现(Lua接近). (2认同)

Mat*_*ira 23

_是有效的标识符.标识符不能只包含下划线,它们也可以是下划线.

_ = o = Object.new
_.object_id == o.object_id
# => true
Run Code Online (Sandbox Code Playgroud)

您还可以将其用作方法名称:

def o._; :_ end
o._
# => :_
Run Code Online (Sandbox Code Playgroud)

当然,它不是一个可读的名称,也不会向读者传递有关变量引用的内容或方法的作用的任何信息.

IRB,特别是,设置_为最后一个表达式的值:

$ irb
> 'asd'
# => "asd"
> _
# => "asd"
Run Code Online (Sandbox Code Playgroud)

就像它在源代码中一样,它只是设置_为最后一个值:

@workspace.evaluate self, "_ = IRB.CurrentContext.last_value"
Run Code Online (Sandbox Code Playgroud)

做了一些存储库探索.这是我发现的:

在文件的最后一行id.c,有一个调用:

REGISTER_SYMID(idUScore, "_");
Run Code Online (Sandbox Code Playgroud)

grep来源给idUScore了我两个看似相关的结果:

shadowing_lvar_gen似乎是一种机制,通过该机制,块的形式参数替换了另一个范围中存在的同名变量.似乎提出"重复参数名称" SyntaxError和"阴影外部局部变量"警告的功能.

grep获取源代码后shadowing_lvar_gen,我在Ruby 1.9.3的更新日志中找到了以下内容:

Tue Dec 11 01:21:21 2007 Yukihiro Matsumoto

  • parse.y(shadowing_lvar_gen):"_"没有重复的错误.

这很可能是这条线的起源:

if (idUScore == name) return name;
Run Code Online (Sandbox Code Playgroud)

由此可见,我推断出在某种情况下proc { |_, _| :x }.call :a, :b,一个_变量只会影响另一个变量.


这是有问题的提交.它基本上介绍了这两行:

if (!uscore) uscore = rb_intern("_");
if (uscore == name) return;
Run Code Online (Sandbox Code Playgroud)

idUScore显然,从一个甚至不存在的时候开始.

  • 这根本不解释*为什么`lambda {| _,_ | 42}```lambda {| x,x | 42}`不. (6认同)