[ruby] _ (밑줄) 변수는 어디에 어떻게 지정됩니까?

대부분은 _IRB에서 마지막 반환 값에 대한 홀더로서 ‘의 특별한 의미를 알고 있지만, 이것이 제가 여기서 묻는 것이 아닙니다 .

대신 _평범한 루비 코드에서 변수 이름으로 사용되는 경우 에 대해 묻습니다 . 여기서는 “Do n’t care 변수”(à la Prolog ) 와 유사한 특별한 동작이있는 것으로 보입니다 . 다음은 고유 한 동작을 보여주는 몇 가지 유용한 예입니다.

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에서 직접 실행되었습니다 (가 추가 된 상태).

이 행동에 대한 문서를 어디서도 찾을 수 없기 때문에 이것은 모두 내 실험의 결과입니다 (분명히 검색하기 가장 쉬운 것은 아닙니다). 궁극적으로이 모든 것이 내부적으로 어떻게 작동하는지 궁금해서 .NET의 특별한 점을 정확히 이해할 수 있습니다 _. 그래서 문서에 대한 참조를 요청하고, 바람직하게는 Ruby 에서 동작 하는 방식을 보여주는 Ruby 소스 코드 (및 아마도 RubySpec )를 요청합니다 _.

참고 :이 대부분은 @Niklas B 와의 토론 에서 비롯되었습니다.



답변

“중복 인수 이름”오류를 억제하기 위해 소스에 몇 가지 특수 처리가 있습니다. 에 나타납니다에만 오류 메시지 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루프 의 두 번째 줄에서 경고가 표시되지 않음을 알 수 있습니다.

_1.9.3 소스에서 찾을 수있는 유일한 특수 처리 는 위의 것입니다. 중복 이름 오류가 억제되고 사용되지 않는 변수 경고가 억제됩니다. 이 두 가지 외에는 다른 것과 _마찬가지로 평범한 오래된 변수입니다. .NET의 (사소한) 특수성에 대한 문서를 모릅니다 _.

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은 상황을 약간 느슨하게합니다.


답변

_유효한 식별자입니다. 식별자는 밑줄을 포함 할 수있을뿐만 아니라 밑줄이 될 수도 있습니다 .

_ = 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두 가지 관련성있는 결과를 얻었습니다.

shadowing_lvar_gen블록의 형식 매개 변수가 다른 범위에 존재하는 동일한 이름의 변수를 대체하는 메커니즘 인 것 같습니다. “중복 된 인자 이름” SyntaxError과 “섀도 잉 외부 지역 변수”경고를 발생 시키는 것으로 보이는 함수입니다 .

grep대한 소스를 검색 한 후 Ruby 1.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에서 한 _변수가 다른 변수를 단순히 그림자 로 만든다고 추론합니다 .


문제의 커밋은 다음과 같습니다 . 기본적으로 다음 두 줄을 도입했습니다.

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

idUScore분명히 존재조차하지 않았던 시대부터 .


답변