[regex] 왜 Ruby에서 Regexp 객체가 “거짓”으로 간주됩니까?

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

루비 에는 부울 객체에 대한 두 개의 특정 클래스가 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'

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



답변

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

if /foo/
  whatever
end

효과적으로된다

if /foo/ =~ $_
  whatever
end

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

warning: regex literal in condition

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

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

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


답변

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

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하고이 값을 명시 적으로 정규 표현식 검사를 수행하는 것이 더 일반적 입니다.

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


답변