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

时间:2012-03-04 22:46:42

标签: 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

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

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

注意:大部分内容来自this discussion @Niklas B.

2 个答案:

答案 0 :(得分:51)

源中有一些特殊处理可以抑制“重复参数名称”错误。该错误消息仅显示在shadowing_lvar_gen the 1.9.3 version looks like this {/ 3}}内的parse.y中:

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

idUScoredefined in 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循环的第二行被禁止。

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

在Ruby 2.0中,warn_unused_var中的idUScore == v[i]测试被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 :(得分:22)

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

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

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

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

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

特别是

IRB_设置为最后一个表达式的值:

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

由于它是in the source code,因此只需将_设置为最后一个值:

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

有些存储库正在探索。这是我发现的:

在文件id.c的最后一行,有一个电话:

REGISTER_SYMID(idUScore, "_");
<grep idUScore的来源给了我两个看似相关的结果:

shadowing_lvar_gen似乎是一种机制,通过该机制,块的形式参数替换了另一个范围中存在的同名变量。这个函数似乎引发了“重复的参数名称”SyntaxError和“阴影外部局部变量”警告。

grep shadowing_lvar_gen来源之后,我找到了以下on the changelog for Ruby 1.9.3

  

Tue Dec 11 01:21:21 2007 Yukihiro Matsumoto

     
      
  • parse.y(shadowing_lvar_gen):“_”没有重复错误。
  •   

这可能是this line的起源:

if (idUScore == name) return name;

由此我推断,在proc { |_, _| :x }.call :a, :b这样的情况下,一个_变量只会影响另一个变量。


这是the commit in question。它基本上介绍了这两行:

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

idUScore甚至根本不存在的时间开始。