web-dev-qa-db-ja.com

Rubyに単一のブールクラスの代わりにTrueClassとFalseClassがあるのはなぜですか?

これについてわかったとき、私は値のシリアル化に取り組んでいました。 Rubyには TrueClass クラスと FalseClass クラスがありますが、Booleanクラスです。これがなぜなのか知りたいのですが。

Booleanを使用することにはいくつかの利点があります。たとえば、文字列の構文解析を一元化できます。

Ruby開発者は私より頭が良いので、私が見ないだけの理由はたくさんあるに違いありません。しかし、今のところ、 OneClass の代わりにTwoClassFixnumを持っているように見えます。

73
kikito

マッツ自身が2004年に メーリングリストメッセージ でこの質問に回答したようです。

彼の答えの短いバージョン:「現時点では問題なく動作し、ブール値を追加してもメリットはありません。」.

個人的には私はそれに同意しません。前述の「文字列解析」はその一例です。もう1つは、「ブール」クラスを持つ変数(ymlパーサーなど)に応じて変数に異なる処理を適用する場合に便利です。1つの「if」が削除されます。見た目もより正確ですが、それは個人的な意見です。

20
kikito

クラスの目的は、類似したオブジェクト、または類似した動作を持つオブジェクトをグループ化することです。 12は非常に似ているため、同じクラスに属していることは完全に理にかなっています。 truefalseは似ていますが似ていません。実際、それらの全体は、それらが正確に互いに反対であり、反対の動作をするということです。したがって、それらは同じクラスに属していません。

Booleanクラスに実装する一般的な動作の例を挙げていただけますか?何も考えられない。

TrueClassFalseClassの振る舞いを見てみましょう。正確にfourメソッドがあります。もういや。そして、すべての単一のケースで、2つのメソッドは正確に反対を行います。どのようにそしてなぜあなたはそれを単一のクラスに入れますか?

これらのメソッドをすべて実装する方法は次のとおりです。

class TrueClass
  def &(other)
    other
  end

  def |(_)
    self
  end

  def ^(other)
    !other
  end

  def to_s
    'true'
  end
end

そして今、その逆です:

class FalseClass
  def &(_)
    self
  end

  def |(other)
    other
  end

  def ^(other)
    other
  end

  def to_s
    'false'
  end
end

確かに、Rubyでは舞台裏で起こっている多くの「魔法」があり、実際にはTrueClassFalseClassによって処理されるのではなく、インタープリターに組み込まれています。 if&&||!など。ただし、Smalltalkでは、RubyがFalseClassおよびTrueClassの概念を含めて多くを借用しました)では、これらすべてがメソッドとしても実装されており、 Rubyでも同じことができます。

class TrueClass
  def if
    yield
  end

  def ifelse(then_branch=->{}, _=nil)
    then_branch.()
  end

  def unless
  end

  def unlesselse(_=nil, else_branch=->{})
    ifelse(else_branch, _)
  end

  def and
    yield
  end

  def or
    self
  end

  def not
    false
  end
end

そして再びその逆:

class FalseClass
  def if
  end

  def ifelse(_=nil, else_branch=->{})
    else_branch.()
  end

  def unless
    yield
  end

  def unlesselse(unless_branch=->{}, _=nil)
    ifelse(_, unless_branch)
  end

  def and
    self
  end

  def or
    yield
  end

  def not
    true
  end
end

数年前、私は楽しみのために上記を書き、 公開されていても を書きました。 Rubyはメソッドのみを使用しながら特殊な演算子を使用するため、構文が異なるという事実は別として、Rubyの組み込み演算子とまったく同じように動作します。実際、実際には RubySpec準拠testsuite私の構文に移植 とパスします。

60
Jörg W Mittag

trueとfalseは、複数の値を保持するブールクラスで管理できますが、その場合、クラスオブジェクトは内部値を持つ必要があるため、使用するたびに逆参照する必要があります。

代わりに、Rubyは、trueとfalseを長い値(0と1)として扱います。それぞれの値は、オブジェクトクラスのタイプ(FalseClassとTrueClass)に対応します。単一のブール値の代わりに2つのクラスを使用することによってクラス、各クラスは値を必要としないため、そのクラス識別子(0または1)によって簡単に区別できます。これは、Rubyエンジンの内部で速度が大幅に向上することにつながります。 Rubyは、TrueClassとFalseClassを、ID値からのゼロ変換を必要とする長い値として扱うことができますが、ブールオブジェクトは、評価する前に逆参照する必要があります。

4
Asher

Rubyフォーラム(2013) でMatzを引用:

... trueとfalseが共通に共有するものは何もないため、ブールクラスはありません。それ以外に、Rubyではすべてがブール値として動作します...

4
steenslag

falsenil以外はすべてデフォルトでRuby)でtrueに評価されるため、Stringに解析を追加するだけで済みます。

このような何かがうまくいくかもしれません:

class Object

  ## Makes sure any other object that evaluates to false will work as intended,
  ##     and returns just an actual boolean (like it would in any context that expect a boolean value).
  def trueish?; !!self; end

end

class String

  ## Parses certain strings as true; everything else as false.
  def trueish?
    # check if it's a literal "true" string
    return true if self.strip.downcase == 'true'

    # check if the string contains a numerical zero
    [:Integer, :Float, :Rational, :Complex].each do |t|
      begin
        converted_number = Kernel.send(t, self)
        return false if converted_number == 0
      rescue ArgumentError
        # raises if the string could not be converted, in which case we'll continue on
      end
    end

    return false
  end

end

使用すると、次のようになります。

puts false.trueish?   # => false
puts true.trueish?    # => true
puts 'false'.trueish? # => false
puts 'true'.trueish?  # => true
puts '0'.trueish?     # => false
puts '1'.trueish?     # => true
puts '0.0'.trueish?   # => false
puts '1.0'.trueish?   # => true

Ruby=の背後にある「大きなアイデア」の一部は、独自のネームスペースに存在する完全にカプセル化されたクラスを作成するのではなく、希望する動作をプログラムに固有のものにすることです(ブール解析など)。ワールド(例:BooleanParser)。

3

Ruby nilとfalseはfalseであり、その他はすべてtrueであるため、特定のブールクラスは必要ありません。

あなたはそれを試すことができます :

if 5
  puts "5 is true"
end

5はtrueと評価されます

if nil
    puts "nil is true"
else
    puts "nil is false"
end

「nil is false」を出力します

1
Mongus Pong

主な理由は、ブール式を実装するほうが、変換を意味するブールクラスを使用する場合よりもはるかに速く簡単であることです。

Mongus Pongが言ったように、 "if"と書くと、インタプリタにthingを評価してから分岐するように依頼します。ブールクラスがあった場合、評価thingをブールbeforeブランチに変換する必要があります(もう1ステップ)。

このような->ブール変換は、ブールクラスのRubyメソッドとして使用できます。このメソッドは、他のRubyメソッドと同様に動的に変更できます。 、開発者が物事を完全に台無しにすることを許可します(実際にはそれほど深刻ではありません)。

Ruby( "send"メソッドの処理を思い出してください)...).

1
Chucky