Is it possibile to override boolean method like true and false in Ruby?
For example
def true
rand()
end
def false
rand()
end
true #=> 0.7548583661438558
false #=> 0.19239076750079454
If I try the above, I get:
true #=> true
false #=> false
I'm trying to understand where these true, false methods are defined.
I know that && and || can't be overriden, because && and || are short circut operators.
Are true and false similar? or they are written inside Ruby somewhere?
I'm just experimenting with Ruby.
true and false cannot be overridden because they are keywords, not methods. Their values are hard-coded and there's no way to re-assign a different value. (at least not from within Ruby)
Even when implementing a method with the same name, a literal true will still resolve to the built-in value.
What you can do is change the object, true is resolving to for interesting results: (this is just for fun, don't do this in actual code)
def true.inspect
rand.to_s
end
true #=> 0.8499100640573652
true #=> 0.22279583303254913
However, the above example doesn't actually change the value of true in any way. Also, don't think of true as a method invocation returning a random float every time it is called. We are looking at the very same object:
a = true
a #=> 0.5813338288658235
a #=> 0.6447125630746154
What we did is change its representation by overriding inspect, which:
Returns a string containing a human-readable representation of obj.
We see those floats because IRB calls inspect in an attempt to print the last expression's result in a readable way.
If we set IRB's output mode to :to_s, our change is bypassed and we see true again:
IRB.CurrentContext.inspect_mode = :to_s
a #=> true
true #=> true
Related
I have this method
def forest(sword, armour, ring)
with its arguments having true or false values, which I declare in the main program as
forest false, false, false
If during the program sword=true && armour=true, is there any way for Ruby to automatically assess whether the arguments are true or false?
Could I write something like
forest sword-truth-value, armour-truth-value, ring-truth-value?
The program I'm writing is very long and it would take too many code lines to take every single case into consideration.
Thanks for the help!
To achieve what you are looking for, you should wrap the forest method in a class and define each argument as an instance variable.
class Forest
attr_accessor :sword, :armour, :ring
def initialize(sword = false, armour = false, ring = false)
#sword = sword
#armour = armour
#ring = ring
end
end
So now, you can declare an instance of Forest,
forest = Forest.new
All the variables default to false, unless you explicitly write true.
With the attr_accessor, you can access and set all the variables.
forest.sword #=> false
forest.sword = true
forest.sword #=> true
The values true and false are atomic. If you don't want pass them as literal values you have to set a variable, i.e.
is_sword = false
is_armor = false
is_ring = false
forest is_sword, is_armor, is_ring
It seems like that should address your question, but it might be worth introducing the concepts of "mutable" objects as well. For example hashes and arrays are mutable:
hash = { armor: false, sword: false, ring: false }
def delete_nil_values(options)
# delete all key-vals from the hash where the val is falsey
options.each_key { |key| options.delete(key) if !options[key]}
options
end
delete_nil_values(hash)
puts hash
# => {}
# it is empty
This may or may not be what you intend; if you want to write 'side effect free' code you should be aware of this.
If you "clone" the input at the top of the delete_nil_values method using options = Marshal.load(Marshal.dump(options)) then it will be immutable.
To summarize: A function evaluates its arguments at runtime. So its variables will be the same that you passed in but if you have side-effects in your code the variables may have mutated.
I'm working on this exercise on Codewars (https://www.codewars.com/kata/typer-dot-js/), and I'm having trouble writing a type checker for booleans.
Based on my understanding, booleans either return true or false. But I've also read a lot of people saying that every object in Ruby is a boolean except for nil. I tried writing the method in a bunch of different ways, but I'm just not getting it. Below are some of the tries.
class Typer
def self.is_boolean? input
input == true || false
end
def self.is_boolean? input
input.class == TrueClass || FalseClass
end
def self.is_boolean? input
input == nil ? false : true
end
|| doesn't work as you expected. For example,
input == true || false
is testing if
input == true
is truthy, or if
false
is truthy. Note that the latter isn't testing input == false. And that is your main misunderstanding.
Ruby does not have a built-in method to convert values to Boolean. That may be by design, as the only false values in Ruby are false and nil. All other values (empty string, empty array, empty hash, 0) are true. There, however, a "hack" that can be used to convert values to Boolean: itβs called "bang-bang" or "double-bang" and it consists of two Boolean negation operators, like this:
!!nil
=> false
!!false
=> false
!!""
=> true
!!0
=> true
!![]
=> true
!!{}
=> true
http://rubymonk.com/learning/books/1/problems/148-array_of_fixnum
Ruby monk suggests:
def array_of_fixnums?(array)
array.all? { |x| x.is_a? Fixnum }
end
That is fine and all, however the following code works in irb 1.9.2 but fails when rubymonk passes an empty array:
def array_of_fixnums?(array)
result = false
array.each { |n|
if n.is_a? Fixnum
result = true
else
result = false
end }
result
end
here is the irb output:
1.9.2-p320 :001 > array_of_fixnums? []
=> false
and here is what rubymonk says about my solution:
returns 'true' for [1,2,3] β
returns 'false' for ['a',1,:b] β
returns 'true' for []
RSpec::Expectations::ExpectationNotMetError
expected false to be true
I'm wondering why this is so?
Update based on answers:
def array_of_fixnums?(array)
result = true
array.each { |n| return false unless n.is_a? Fixnum }
result
end
Your code has two problems:
The problem is phrased in a slightly vague manner. What they actually want is for you to return false if any of the elements are not Fixnums, and true otherwise β so an empty array should give true. If you look at your code, you'll see that result starts out false, so if the array is empty, it will return false even though the test thinks it should be true. You can solve this by starting out with true.
Your code actually just detects whether the last element of the array is a Fixnum. Let's take the array [1, "nope", 3]. It will first see 1 and set result to true, then it will see "nope" and set result to false, then it will see 3 and set result to true, and that's what the method will return. RubyMonks tests actually don't detect this error, but it would show up in the real world. You want to return immediately after getting a false result, as that is enough to determine that the array is not all Fixnums.
For this case you will never enter the each cycle as there are no elements in array. So you return the defualt value of result that you set to false on the line above. However if there are no elements in an array, then all of its elements are Fixnums and so you should return true.
Is there a simple way in Ruby to get a true/false value from something without explicitly evaluating it to true or false
e.g. how would one more succinctly express
class User
def completed_initialization?
initialization_completed == 1 ? true : false
end
end
is there some way to do something along the lines of
initialization_completed.true?
There's obviously not much in it but since I'm in the zen garden of Ruby I might as well embrace it
EDIT (I've updated the example)
This question was extremely badly phrased as was very gently pointed out by #Sergio Tulentsev. The original example (below) does of course evaluate directly to a boolean. I'm still struggling to find an example of what I mean however Sergio's double-negative was in fact exactly what I was looking for.
Original example
class User
def top_responder
responses.count > 10 ? true : false
end
end
> operator already returns boolean value. So it can be just
def top_responder
responses.count > 10
end
To convert arbitrary values to booleans, I offer you this little double-negation trick.
t = 'foo'
!!t # => true
t = 1
!!t # => true
t = 0
!!t # => true
t = nil
!!t # => false
The first negation "casts" value to boolean and inverts it. That is, it will return true for nil / false and false for everything else. We need another negation to make it produce "normal" values.
Just wondering what !! is in Ruby.
Not not.
It's used to convert a value to a boolean:
!!nil #=> false
!!"abc" #=> true
!!false #=> false
It's usually not necessary to use though since the only false values to Ruby are nil and false, so it's usually best to let that convention stand.
Think of it as
!(!some_val)
One thing that is it used for legitimately is preventing a huge chunk of data from being returned. For example you probably don't want to return 3MB of image data in your has_image? method, or you may not want to return your entire user object in the logged_in? method. Using !! converts these objects to a simple true/false.
It returns true if the object on the right is not nil and not false, false if it is nil or false
def logged_in?
!!#current_user
end
! means negate boolean state, two !s is nothing special, other than a double negation.
!true == false
# => true
It is commonly used to force a method to return a boolean. It will detect any kind of truthiness, such as string, integers and what not, and turn it into a boolean.
!"wtf"
# => false
!!"wtf"
# => true
A more real use case:
def title
"I return a string."
end
def title_exists?
!!title
end
This is useful when you want to make sure that a boolean is returned. IMHO it's kind of pointless, though, seeing that both if 'some string' and if true is the exact same flow, but some people find it useful to explicitly return a boolean.
Note that this idiom exists in other programming languages as well. C didn't have an intrinsic bool type, so all booleans were typed as int instead, with canonical values of 0 or 1. Takes this example (parentheses added for clarity):
!(1234) == 0
!(0) == 1
!(!(1234)) == 1
The "not-not" syntax converts any non-zero integer to 1, the canonical boolean true value.
In general, though, I find it much better to put in a reasonable comparison than to use this uncommon idiom:
int x = 1234;
if (!!x); // wtf mate
if (x != 0); // obvious
It's useful if you need to do an exclusive or. Copying from Matt Van Horn's answer with slight modifications:
1 ^ true
TypeError: can't convert true into Integer
!!1 ^ !!true
=> false
I used it to ensure two variables were either both nil, or both not nil.
raise "Inconsistency" if !!a ^ !!b
It is "double-negative", but the practice is being discouraged. If you're using rubocop, you'll see it complain on such code with a Style/DoubleNegation violation.
The rationale states:
As this is both cryptic and usually redundant, it should be avoided
[then paraphrasing:] Change !!something to !something.nil?
Understanding how it works can be useful if you need to convert, say, an enumeration into a boolean. I have code that does exactly that, using the classy_enum gem:
class LinkStatus < ClassyEnum::Base
def !
return true
end
end
class LinkStatus::No < LinkStatus
end
class LinkStatus::Claimed < LinkStatus
def !
return false
end
end
class LinkStatus::Confirmed < LinkStatus
def !
return false
end
end
class LinkStatus::Denied < LinkStatus
end
Then in service code I have, for example:
raise Application::Error unless !!object.link_status # => raises exception for "No" and "Denied" states.
Effectively the bangbang operator has become what I might otherwise have written as a method called to_bool.
Other answers have discussed what !! does and whether it is good practice or not.
However, none of the answers give the "standard Ruby way" of casting a value into a boolean.
true & variable
TrueClass, the class of the Ruby value true, implements a method &, which is documented as follows:
Returns false if obj is nil or false, true otherwise.
Why use a dirty double-negation when the standard library has you covered?