A float subclass that can be evaluated to false (fuzzy logic) in Ruby
-
21-02-2021 - |
Pergunta
I need a class that can represent probabilities. It can be represented like a float between 0 and 1, and anything below 0.5 should evaluate to false. (or it can be between 1 and -1, anything negative is false)
p = A.probability()
puts p # will output 0.3
if(p)
puts 'success'
else
puts 'failure'
end
# will outputs 'failure'
From this post it seems to suggest it is possible: every object has a boolean value... Most objects in Ruby will have a boolean value of true. Only two objects have a boolean value of false. Just that I need to set this boolean value somehow. So is this possible?
Solução
I wanted to do something similar, but unfortunately this is not possible in Ruby.
The only two objects that have a boolean value of false are false
itself and nil
. A few weeks ago I had a an internet chat (IRC) discussion with Brixen, one of the main developers of rubinius, and he explained it pretty well. My understanding is that when Ruby decides whether an object is true or not, it does NOT call any methods on (i.e. send any messages to) the object, it simply looks at the pointer it has to the object to see whether the pointer itself is equal to false or nil. Since it doesn't call any methods on the object, there is no way for you to change the behavior.
The best you can do is something like this:
p = A.probability()
puts p # => 0.3
if p.to_bool
puts 'success'
else
puts 'failure'
end
Outras dicas
There is no "cast to boolean" operator because, as David notes, falsiness is hard wired for false
and nil
and anything that isn't "falsy" is true. However, there is a logical negation operator so you can do it if you don't mind an explicit cast to boolean:
class P
attr_accessor :p
def !@
p < 0.5
end
end
And then:
>> p = P.new
>> p.p = 0.75
=> puts 'Pancakes!' if(!!p)
Pancakes!
>> p.p = 0.25
>> puts 'Pancakes!' if(!!p)
# No pancakes for you
All the double bangs might be a bit ugly but, on the upside, it might make people notice that something a little non-standard is going on.
First of all, you do not need a Float subclass. Those interested in subclassing Numeric types can see Katzuda's answer.
The simple way to achieve what you want to achieve is to simply use a method:
class P
def initialize p; @p = p
raise "outta range" unless 0 <= @p and @p <= 1 end
def success?; SUCC <= @p end
def fail?; not success? end
end
p = P.new 0.3
p.success? # => false
p.fail? # => true
q = P.new 0.7
q.success? # => true
q.fail? # => false
Remember, for these kind of circumstances, Ruby has methods.