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


81

대부분은 _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 와의 토론 에서 비롯되었습니다.

답변:


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루프 의 두 번째 줄에서 경고가 표시되지 않음을 알 수 있습니다.

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


1
lambda { |_, _| _ }.call(4, 2)1.8과 1.9 사이의 동작 차이 가 의도하지 않은 부작용 인지 궁금합니다 . 변수 이름을 복제 할 수없는 "일반적인"상황에서와 같이 할당 된 순서는 중요하지 않습니다.
Andrew Marshall

3
@AndrewMarshall : 예, "4 대 2"문제는 1.8과 1.9가 스택을 처리하는 방법의 인공물이라고 생각합니다. 눈에 띄는 유일한 시간 |_,_,...|은 중복 오류가 억제 되었기 때문입니다.
MU이 너무 짧

2
@AndrewMarshall : 모두가 우리 뒤에서 서로의 변경 로그를 읽고 있는지 궁금합니다.
MU이 너무 짧

2
좋은 발견. 나는 그것이 이중 매개 변수 이름 오류를 억제하는 것과 같이 단순한 것이라고 가정했다. 루비는 정말 거대한 엉망입니다 : D
니클라스 B.

2
@mu : 물론, 나는 아직 통역 언어의 정말 깨끗한 구현을 보지 못했습니다 (Lua가 가까이 다가옵니다).
Niklas B. 2012 년

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

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분명히 존재조차하지 않았던 시대부터 .


6
이 설명하지 않습니다 전혀 이유 lambda { |_, _| 42 }동안 작동 lambda { |x, x| 42 }하지 않습니다.
Andrew Marshall

@AndrewMarshall, 당신이 옳은 것 같습니다. |_, _|작동하지만 |__, __|작동하지 않습니다. _특별한 의미가있는 것 같습니다. 루비 소스에서 정보를 찾을 수 있는지 살펴 보겠습니다.
Matheus Moreira 2011 년

1
그건 그렇고, 귀하의 업데이트는 유익하지만 IRb에만 적용되므로 관련이 없습니다.
Andrew Marshall

1
변경 로그 항목을 파헤 치려면 +1하십시오. 모두가 C 해커 :)해야
MU가 너무 짧은

1
IRB 소스 코드의 경우 +1. IRB.CurrentContext.last_value는 제시된 질문과 관련이 없더라도 알면 매우 흥미 롭습니다. IRB 밑줄에 대한 Google 검색에서 여기까지 왔습니다.
조쉬
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.