[regex] 왜 Ruby에서 Regexp 객체가 “거짓”으로 간주됩니까?
루비에는 ” 진실성 “과 ” 거짓 “에 대한 보편적 인 아이디어가 있습니다.
루비 에는 부울 객체에 대한 두 개의 특정 클래스가 TrueClass
있으며 FalseClass
, 단일 인스턴스는 특수 변수 true
및 false
로 각각 표시됩니다 .
그러나 진실성 과 허위 는이 두 클래스의 인스턴스로 제한되지 않으며, 개념은 보편적 이며 Ruby의 모든 단일 객체에 적용됩니다. 모든 사물은 진실 하거나 거짓 입니다. 규칙은 매우 간단합니다. 특히, 두 개의 객체 만 거짓입니다 .
nil
의 싱글 인스턴스NilClass
및false
의 싱글 톤 인스턴스FalseClass
다른 모든 대상 은 진실 합니다. 여기에는 다른 프로그래밍 언어에서 허위 로 간주되는 객체도 포함 됩니다.
이 규칙은 언어에 내장되어 있으며 사용자가 정의 할 수 없습니다. to_bool
암시 적 변환이나 이와 유사한 것은 없습니다 .
다음은 ISO Ruby 언어 사양 에서 인용 한 것입니다 .
6.6 부울 값
객체는 하나에 분류된다 trueish 객체 또는 falseish 객체 .
false 와 nil 만 거짓 개체입니다. false 는 클래스의 유일한 인스턴스
FalseClass
(15.2.6 참조)이며 허위 표현식이 평가됩니다 (11.5.4.8.3 참조). 전무는 클래스의 전용 인스턴스NilClass
A (15.2.4 참조)되는 무 발현을 평가하여 (11.5.4.8.2 참조).false 및 nil 이외의 오브젝트 는 실제 오브젝트로 분류됩니다. 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.1 및 JRuby 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('')
)의 경우이 특별한 해석은 적용되지 않습니다.
//
진실하다. 단지 다른 모든 외에 루비 객체와 같은 nil
과 false
.
명령 행에서 직접 (예 : -e
플래그를 사용하여 ) 루비 스크립트를 실행하지 않는 한 , 루비 파서는 그러한 사용법에 대한 경고를 표시합니다 :
경고 : 정규 표현식 리터럴
당신은 수 같은과, 스크립트에서이 동작을 사용합니다
puts "Do you want to play again?"
gets
# (user enters e.g. 'Yes' or 'No')
/y/i ? play_again : back_to_menu
…하지만 로컬 변수를 결과에 할당 gets
하고이 값을 명시 적으로 정규 표현식 검사를 수행하는 것이 더 일반적 입니다.
특히 리터럴 값으로 정의 된 경우 빈 정규 표현식 으로이 검사를 수행하는 유스 케이스를 알지 못합니다 . 강조한 결과는 실제로 대부분의 루비 개발자를 보호합니다.