왜 Ruby에서 Regexp 객체가 "거짓"으로 간주됩니까?


16

루비에는 " 진실성 "과 " 거짓 "에 대한 보편적 인 아이디어가 있습니다.

루비 에는 부울 객체에 대한 두 개의 특정 클래스가 TrueClass있으며 FalseClass, 단일 인스턴스는 특수 변수 truefalse로 각각 표시됩니다 .

그러나 진실성허위 는이 두 클래스의 인스턴스로 제한되지 않으며, 개념은 보편적 이며 Ruby의 모든 단일 객체에 적용됩니다. 모든 사물은 진실 하거나 거짓 입니다. 규칙은 매우 간단합니다. 특히, 두 개의 객체 만 거짓입니다 .

다른 모든 대상진실 합니다. 여기에는 다른 프로그래밍 언어에서 허위 로 간주되는 객체도 포함 됩니다.

이 규칙은 언어에 내장되어 있으며 사용자가 정의 할 수 없습니다. to_bool암시 적 변환이나 이와 유사한 것은 없습니다 .

다음은 ISO Ruby 언어 사양 에서 인용 한 것입니다 .

6.6 부울 값

객체는 하나에 분류된다 trueish 객체 또는 falseish 객체 .

falsenil거짓 개체입니다. false 는 클래스의 유일한 인스턴스 FalseClass(15.2.6 참조)이며 허위 표현식이 평가됩니다 (11.5.4.8.3 참조). 전무는 클래스의 전용 인스턴스 NilClassA (15.2.4 참조)되는 무 발현을 평가하여 (11.5.4.8.2 참조).

falsenil 이외의 오브젝트 는 실제 오브젝트로 분류됩니다. true 는 클래스의 유일한 인스턴스 TrueClass(15.2.5 참조)이며 실제 표현식은 평가됩니다 (11.5.4.8.3 참조).

실행 가능한 Ruby / Spec은 다음과 같은 것으로 보입니다 .

it "considers a non-nil and non-boolean object in expression result as true" do
  if mock('x')
    123
  else
    456
  end.should == 123
end

이 두 가지 출처에 따르면, 나는 Regexp또한 진실 하다고 가정 하지만 내 테스트에 따르면 그들은 그렇지 않습니다.

if // then 'Regexps are truthy' else 'Regexps are falsy' end
#=> 'Regexps are falsy'

나는 이것을 YARV 2.7.0-preview1 , TruffleRuby 19.2.0.1JRuby 9.2.8.0에서 테스트했습니다 . 세 가지 구현 모두 서로 동의하고 ISO Ruby 언어 사양 및 Ruby / Spec에 대한 나의 해석에 동의하지 않습니다.

보다 정확하게 말하면 리터럴Regexp 을 평가 한 결과 개체 는 허위 인 반면 다른 표현의 결과 인 개체는 진실입니다 .Regexp Regexp

r = //
if r then 'Regexps are truthy' else 'Regexps are falsy' end
#=> 'Regexps are truthy'

이것은 버그입니까, 아니면 원하는 동작입니까?


흥미로운 것은 사실입니다 Regex.new("a").
mrzasa

!!//거짓이지만 !!/r/사실입니다. 참 이상합니다.
최대

@max !!/r/false(RVM) Ruby 2.4.1을 사용하여 나를 위해 생성 합니다 .
3limin4t0r

내 나쁜 @ 3limin4t0r 죄송합니다. 네 말이 맞아 나는 느낌표를 남기지 않는 것처럼 정말 어리석은 짓을 했어야했다.
최대

2
가설은 //in if // thenif //=~nil thenRegexp 인스턴스가 아니라 테스트 (바로 가기에 대한 바로 가기 ) 로 해석 된다고 생각 합니다.
Casimir et Hippolyte

답변:


6

이것은 버그가 아닙니다. 루비는 코드를 다시 작성하여

if /foo/
  whatever
end

효과적으로된다

if /foo/ =~ $_
  whatever
end

이 코드를 일반 스크립트에서 실행하고 -e옵션을 사용하지 않는 경우 경고가 표시됩니다.

warning: regex literal in condition

이것은 아마도 대부분 혼란 스럽기 때문에 경고가 표시되지만 -e옵션을 사용하여 한 줄에 유용 할 수 있습니다 . 예를 들어 파일에서 주어진 정규 표현식과 일치하는 모든 줄을 인쇄 할 수 있습니다.

$ ruby -ne 'print if /foo/' filename

(기본 인수는 print것입니다 $_뿐만 아니라.)


참조 -n, -p, -a-l옵션과 만 사용할 때 커널 방법의 소수 -n또는 -p사용 ( chomp, chop, gsubsub).
matt

파서 에는 경고가 발생 하는 두 번째 부분 도 있습니다 . 그래도 무슨 일이 일어나고 있는지 모르겠습니다.
matt

나는 "두 번째 부분"이 실제로이 질문에 적용되는 것이라고 생각합니다. NODE_LIT유형으로 T_REGEXP. 귀하의 답변에 게시 한 것은 동적 Regexp리터럴 , 즉 Regexp보간을 사용 하는 리터럴입니다 /#{''}/.
Jörg W Mittag

@ JörgWMittag 당신이 옳다고 생각합니다. 컴파일러와 생성 된 바이트 코드에서 파킹하면 동적 정규 표현식의 경우 구문 분석 트리가 $_컴파일러가 정상적으로 처리하는 노드 로 명시 적으로 추가 되도록 다시 작성되는 것처럼 보입니다 . 정적 경우에는 모두 처리됩니다. 컴파일러. “여기서 파스 트리가 다시 쓰여진 곳을 볼 수 있습니다”가 좋은 대답을하기 때문에 부끄러운 일입니다.
matt

4

이것은 루비 언어의 문서화되지 않은 기능의 결과입니다 . 이 사양에 의해 가장 잘 설명됩니다 .

it "matches against $_ (last input) in a conditional if no explicit matchee provided" do
  -> {
    eval <<-EOR
    $_ = nil
    (true if /foo/).should_not == true
    $_ = "foo"
    (true if /foo/).should == true
    EOR
  }.should complain(/regex literal in condition/)
end

일반적으로 $_"마지막으로 읽은 문자열 gets"

문제를 더 혼란스럽게 만들려면 $_(와 함께 $-) 전역 변수 가 아닙니다 . 로컬 범위가 있습니다.


루비 스크립트가 시작되면 $_ == nil.

따라서 코드 :

// ? 'Regexps are truthy' : 'Regexps are falsey'

다음과 같이 해석됩니다.

(// =~ nil) ? 'Regexps are truthy' : 'Regexps are falsey'

... 거짓을 반환합니다.

반면, 문맹 이 아닌 정규 표현식 (예 : r = //또는 Regexp.new(''))의 경우이 특별한 해석은 적용되지 않습니다.

//진실하다. 단지 다른 모든 외에 루비 객체와 같은 nilfalse.


명령 행에서 직접 (예 : -e플래그를 사용하여 ) 루비 스크립트를 실행하지 않는 한 , 루비 파서는 그러한 사용법에 대한 경고를 표시합니다 :

경고 : 정규 표현식 리터럴

당신은 같은과, 스크립트에서이 동작을 사용합니다

puts "Do you want to play again?"
gets
# (user enters e.g. 'Yes' or 'No')
/y/i ? play_again : back_to_menu

...하지만 로컬 변수를 결과에 할당 gets하고이 값을 명시 적으로 정규 표현식 검사를 수행하는 것이 더 일반적 입니다.

특히 리터럴 값으로 정의 된 경우 정규 표현식 으로이 검사를 수행하는 유스 케이스를 알지 못합니다 . 강조한 결과는 실제로 대부분의 루비 개발자를 보호합니다.


조건부만을 예로 사용했습니다. !// #=> true동일한 동작을 가지며 조건부 상태가 아닙니다. 예상대로 작동하는 부울 컨텍스트 (조건부 여부)를 찾을 수 없습니다.
Jörg W Mittag

@ JörgWMittag 예를 들어 !// ? true : false리턴 을 의미 true합니까? 나는 이것이 다시 같은 요점이라고 생각한다 – 그것은 다음과 같이 해석되고있다 :!(// =~ nil) ? true : false
Tom Lord

$_ = 'hello world'위의 코드를 실행하기 전에 수동으로 설정 하면 다른 결과 // =~ 'hello world'가 나옵니다 nil. 왜냐하면 일치하지는 않습니다 .
Tom Lord

아니요, !// 조건부 조건이 없으면이 없음을 의미 합니다 true. 인용 한 사양 Regexp은 조건부에서 리터럴에 관한 것이지만이 예에서는 조건부가 없으므로이 사양이 적용되지 않습니다.
Jörg W Mittag

2
아 .. 정말 놀랍습니다. 동작은 다음과 같이 연결된 것처럼 보입니다 puts !//; $_ = ''; puts !//.-파서가 매크로처럼 확장하기 때문에 가정합니다. 반드시 조건부 안에있을 필요는 없습니까?
Tom Lord
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.