RubyでRegexpオブジェクトが「偽物」と見なされるのはなぜですか?


16

Rubyには「真実性」と「虚偽」という普遍的な考え方があります。

Rubyに、ブールオブジェクト用に2つの特定のクラスがTrueClassありFalseClass、シングルトンインスタンスはそれぞれ特殊変数truefalseで示されます。

ただし、真実性偽造はこれら2つのクラスのインスタンスに限定されず、概念は普遍的であり、Rubyのすべてのオブジェクトに適用されます。すべてのオブジェクトは真実偽物です。ルールは非常に簡単です。特に、2つのオブジェクトだけが偽物です:

  • nilシングルトンインスタンスNilClass
  • false、のシングルトンインスタンス FalseClass

他のすべてのオブジェクト真実です。これには、他のプログラミング言語で偽物と見なされるオブジェクトも含まれます。

これらのルールは言語に組み込まれており、ユーザーが定義することはできません。to_bool暗黙的な変換やそれに類似したものはありません。

ISO Ruby言語仕様からの引用は次のとおりです。

6.6ブール値

オブジェクトは、真実のオブジェクトまたは偽のオブジェクトに分類されます

falsenilのみが偽りのオブジェクトです。falseは、false式が評価されるクラスのインスタンスFalseClass(15.2.6を参照)のみです(11.5.4.8.3を参照)。nilは、クラスの唯一のインスタンス(15.2.4を参照)であり、nil式が評価されます(11.5.4.8.2を参照)。NilClass

falseおよびnil以外のオブジェクトは、trueishオブジェクトに分類されます。trueは、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

これらの2つの情報源によると、私はRegexpsも真実であると想定していますが、私のテストによると、そうではありません。

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

YARV 2.7.0-preview1TruffleRuby 19.2.0.1、およびJRuby 9.2.8.0でこれをテストしました。3つの実装すべてが互いに同意し、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

これはバグではありません。Rubyがコードを書き換えて、

if /foo/
  whatever
end

効果的になる

if /foo/ =~ $_
  whatever
end

このコードを通常のスクリプトで実行している場合(-eオプションを使用していない場合)、警告が表示されます。

warning: regex literal in condition

これはおそらくほとんどの場合多少混乱を招くため、警告が表示されますが、この-eオプションを使用する1行の場合に役立つことがあります。たとえば、指定した正規表現に一致するすべての行をファイルから出力できます。

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

(デフォルト引数がprintある$_としても。)


参照して-n-p-aおよび-lオプション、ならびに場合にのみ利用可能であるカーネル法の一握り-nまたは-p使用されている(chompchopgsubおよびsub)。
マット

また、その警告が出されるパーサーの2番目の部分もあります。何が起こっているのか分からない。
マット

「第二部」はこの質問に実際に当てはまるものだと思います。NODE_LITタイプ付きT_REGEXP。あなたがあなたの答えに投稿したものは、動的Regexpリテラル、すなわちRegexp補間を使用するリテラル、例えばのもの/#{''}/です。
イェルクWミッターク

@JörgWMittag私はあなたが正しいと思います。コンパイラと生成されたバイトコードを見て回ると、動的正規表現の場合、解析ツリーが書き直され$_、コンパイラが通常どおりに処理するノードとして明示的に追加されますが、静的な場合はすべてコンパイラ。「解析ツリーがどこに書き直されているかがわかる」というのはいい答えです。
マット

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

問題をさらに混乱させるために、$_(とともに$-)はグローバル変数ではありません。ローカルスコープです。


Rubyスクリプトが起動すると、$_ == nil

したがって、コード:

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

次のように解釈されています:

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

...これは偽を返します。

一方、非リテラル正規表現(r = //またはRegexp.new(''))の場合、この特別な解釈は適用されません。

//真実です。niland 以外のルビーの他のすべてのオブジェクトと同じようにfalse


コマンドラインで直接(つまり、-eフラグを使用して)Rubyスクリプトを実行しない限り、Rubyパーサーはそのような使用法に対して警告を表示します。

警告:条件の正規表現リテラル

あなたは可能性のようなもので、スクリプトでこの動作を利用します。

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

...しかし、ローカル変数を結果に割り当て、getsこの値に対して正規表現チェックを明示的に実行するのがより一般的です。

特にリテラル値として定義されている場合、空の正規表現を使用してこのチェックを実行するためのユースケースは知りません。あなたが強調した結果は、確かにほとんどのルビー開発者を油断して捕まえるでしょう。


例として条件付きのみを使用しました。!// #=> true同じ動作をし、条件付きではありません。ブールコンテキスト(条件付きかどうかにかかわらず)が見つかりませんでした。期待どおりに動作します。
イェルクWミッターク

@JörgWMittagたとえば、!// ? true : false返品を意味しますtrueか?私はこれも同じ点だと思います-それは次のように解釈されています:!(// =~ nil) ? true : false
Tom Lord

手動で設定する場合は$_ = 'hello world'、上記のコードを実行する前に、あなたは異なる結果を取得する必要があります-ので// =~ 'hello world'、しかし、一致していませんnil
Tom Lord、

いいえ、!// つまり、条件なしではに評価されtrueます。引用した仕様Regexpは条件付きのリテラルに関するものですが、この例では条件がないため、この仕様は適用されません。
イェルクWミッターク

2
ああ、うん、びっくり。ただし、動作はリンクされputs !//; $_ = ''; puts !//ているようです。-パーサーがマクロのように展開するためだと思います。必ずしも条件内にある必要はありませんか?
Tom Lord、
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.