Odd Ruby If Statement Question - ruby

I've noticed this little oddity(I think) in If Statements in Ruby. Here's an example:
my_number = nil
if my_number < 3
'number is less than 3'
end
Obviously, when you run this code you'll get a "comparison of Fixnum with nil failed" error. Now here's something strange. If I make a little change in the If Statement to check for nil, it works fine. Example:
my_number = nil
if my_number && my_number < 3
'number is less than 3'
end
Adding the check for nil makes it not crash. This may all sound stupid but I can't figure out why that works. Shouldn't it still throw an error?
Thanks to anyone who can explain this. :) Thanks!

Boolean expressions are evaluated in what is known as "short circuit" fashion. That is, as soon as it know the result, it doesn't keep trying to evaluate expressions.
So it does the if my_number and since that's false, there is no need to continue, because false && <anything> is always false.
This is a useful language feature, and many languages work like this (the original Visual Basic is one exception that I can think of) because it lets you do exactly this sort of test without requiring cumbersome nested 'if's.

This is no way specific to Ruby: I've noticed this behaviour in all languages I've ever used, from Pascal to Java.
If you have boolean expression b1 && b2 && b3 ... && bn, most languages guarantee that bi will be evaluated from left to right and if some bi turns out to be false, evaluation will be stopped. (because whole expression is false then). Same for boolean || operator.

It has to be OK to test for nil, so if nil or something like it is OK in almost every language.
But for an arithmetic comparison, if it didn't throw an exception it would have to return true or false. Either way is problematic.
True kind of doesn't make sense, as nil would be both < and > than 3.
False is almost as bad, as now nil < 3 and nil >= 3 are both false, and that's not ideal.
So, the comparison method throws an exception. Problem solved.

nil is "falsy". Try this in irb:
irb(main):001:0> puts "Nil is true " if nil
=> nil
irb(main):002:0> puts "Nil is not true " if !nil
Nil is not true
=> nil
nil isn't the same as false, but making it act as such help a lot in loops and other tests:
false == nil
=> false
Since the first part of your and is false ruby does the lazy thing and doesn't even bother to evaluate the next bit, if it's > 3.

What's happening is called short-circuit evaluation. You're statement can be thought of like this:
if( my_number )
if( my_number < 3)
'number is less than 3'
end
end
Since the first condition is false there's no reason to evaluate the second condition of the statement -- the my_number < 3 part.

Related

How can I compare a nil with an integer?

With Ruby 2.4, I have this in part of my "if" statement
row_data.index{|x| DataHelper.my_function(x) } > num_var
Unfortunately, the above statement dies with an error if "row_data.index{|x| DataHelper.my_function(x) }" evaluates to nil. Is there any way to rewrite the above so that it would return "false" if "row_data.index{|x| DataHelper.my_function(x) }" evaluated to nil? I don't want to store the expression in a variable prior to my "if" statement because I might not need to even execute that statement if execution doesn't reach there. I feel like there's a one-liner but I don't know what it is.
Short circuit evaluations is the right way checking nil or false conditionals for two important reasons.
Easy way out explicit conversions (to_s, to_i etc) can save you momentarily from raised exceptions/errors but at times can play the devil trick to break your conditionals when one of your compared values of the conditional is from - 0, "", [] and etc. Hence, explicit cares are to be taken considering your code might not last longer enough after a point of time.
Ex. - 1
if x.to_i > -1
puts x "is not a negative integer"
else
puts x "is a negative integer"
end
It can be dangerous as nil.to_i :=> 0 gets approved at if x.to_i > -1 logic check entering into conditional block with converted value (0 in case of nil.to_i).
Probably, you won't mind for ex. - 1 to print a nil as: 0 is not a negative integer. How about this?
if x.to_i
puts " 1/x is" 1/x
end
It can further raise ZeroDivisionError: divided by 0 for each x as nil and you have pay extra care in these cases. May be you did not want to entertain nil inside your block from the first place.
Performance and CLEAN Code are two buzz words you hear everytime. Short circuit evaluations (&&) do not bother about succeeding conditionals if preceding condition is falsewhich makes conditionals execute faster. Additionally, It protects nil values entering into your conditional block and make it more vulnerable.
Answer to your ask:
if (not row_data.index{|x| DataHelper.my_function(x) }.nil?) && (row_data.index{|x| DataHelper.my_function(x) } > num_var)
# do your if block here
# this keeps nil away
else
puts "row_data.index{|x| DataHelper.my_function(x) } is nil"
end
You can take advantage of nil.to_i returning 0
if row_data.index{ |x| DataHelper.my_function(x) }.to_i > num_var
# index is bigger than num_var
else
# index is smaller or equal to num_var
end
Depending on what my_function and num_var represent, you may need to also account for the case of num_var == 0.

Ruby: Clean code for checking nil /false conditional statement?

I always meet this Ruby problem, I want to write it more cleanly.
var a can be nil
a.value can also be nil
a.value has possible true or false value
if (not a.nil?) && (not a.value.nil?) && a.value == false
puts "a value is not available"
else
puts "a value is true"
end
The problem is that the conditional statement is too clumsy and hard to read.
How can I improve the checking nil and false conditional statement?
Thanks, I am a Ruby newbie
Ruby on rails has an extension called try which allows you to write:
if a.try(:value) == false
which is very clean. Without try, you can just write
if a && a.value == false
If a.value is nil, it is not false, so that is ok :)
If it is possible that a.value is not defined (which would raise an exception), I would write that as follows:
if a && a.respond_to?(:value) && a.value == false
[UPDATE: after ruby 2.3]
Since ruby 2.3 there is an even shorter version:
if a&.value == false
which is almost equivalent to a.try(:value) (but is pure ruby). Differences:
if value does not exist, the &. operator will throw, try will just return nil (preferable or not?)(note: try! would also throw).
when cascading try or &. they also handle false differently. This follows logically from previous difference, try will return nil, while &. will throw because false knows no methods :P
You can achieve it in more compacted way using Safe Navigation Operator (&.):
if a&.value == false
Source : http://mitrev.net/ruby/2015/11/13/the-operator-in-ruby/
if a && a.value!=false
puts "a value is true"
else
puts "a value is not available"
end
or just
puts a && a.value!=false ? "a value is true" : "a value is not available"
The simplest and cleanest way is to flip it and reverse it. Check for the truthy value rather than the falsey values
if a && a.value
puts "a value is true"
else
puts "a value is not available"
end
Of course in Rails you could do it either way by using blank? or present?
Your condition is redundant. If a.value is to be false, then it would not be nil.
if a.nil?.! && a.value == false
puts "a value is not available"
else
puts "a value is true"
end
This will always return a boolean, if nil it will return false; if false it returns false; if true returns true. Try it out, it's nice and short:
!!a.try(:value) == false

what does nil mean/represent here?

Following are the simple statements in the irb shell. What does nilin the output mean ? Why does it accompany the print statement in the if block ?
irb(main):062:0> if(x==20 && y==30)
irb(main):063:1> print("if statement working !")
irb(main):064:1> else
irb(main):065:1* print("else statement working !!")
irb(main):066:1> end
if statement working !=> nil # what does nil represent here ?
In Ruby, all expressions return values, even if it's nil. Blocks and methods simply return the value of the last expression evaluated. There are many ways to use this effectively. For example, this is the reason explicit returns are often not used. Also, you can do this:
print if x == 20 && y == 30
'if statement working!'
else
'else statement working!'
end
Regarding your example: in addition to printing the string as you instructed, irb will display the value it received from the if-else blocks. Since print always returns nil, both branches will return the same value.
It means that your if-block does not return a value (which it can, actually). For instance, the following is perfectly legal and viable:
foo = if bar > 10
42
else
0
end
# now, foo is either 42 or 0

|| Operator, return when result is known?

I have a function similar to the following:
def check
return 2 == 2 || 3 != 2 || 4 != 5
end
My question is, will Ruby perform all the comparisons even though the first is true, and thus the function return true. My checks are much more intensive, so I'd like to know if I should break this out in a different way to avoid making all the checks every time.
irb(main):004:0> 2 == 2 || 3 != 2 || 4 != 5
=> true
Thank you.
Ruby uses short-circuit evaluation.
This applies to both || and &&.
With || the right operand is not evaluated if the left operand is truthy.
With && the right operand is not evaluated if the left operand is falsy.
|| short-circuits as soon as the first condition is true. So yes, it will help if you put the most expensive conditions at the end.
|| will by default short-circuit evaluate, meaning that once the first "true" expression is encountered it will stop evaluation (unless you explicitly state you want all expressions to evaluate with the 'or' operator).
reference:
http://en.wikipedia.org/wiki/Short-circuit_evaluation
As soon as one of the condition is true, the function will return.
You can test it yourself in irb, like this:
irb> p('Hello') || p('World')
As we know the function p prints its parameters(in an inspect manner) then returns them, so if the || short circuits, only "Hello" is printed, otherwise both "Hello" and "World" are printed.
You can also test the logical && operator, by using puts instead of p, as puts always returns nil.
BTW, irb is a perfect place to play around ruby. You can test everything there, except a small portion of concurrency.

Best ruby idiom for "nil or zero"

I am looking for a concise way to check a value to see if it is nil or zero. Currently I am doing something like:
if (!val || val == 0)
# Is nil or zero
end
But this seems very clumsy.
Objects have a nil? method.
if val.nil? || val == 0
[do something]
end
Or, for just one instruction:
[do something] if val.nil? || val == 0
From Ruby 2.3.0 onward, you can combine the safe navigation operator (&.) with Numeric#nonzero?. &. returns nil if the instance was nil and nonzero? - if the number was 0:
unless val&.nonzero?
# Is nil or zero
end
Or postfix:
do_something unless val&.nonzero?
If you really like method names with question marks at the end:
if val.nil? || val.zero?
# do stuff
end
Your solution is fine, as are a few of the other solutions.
Ruby can make you search for a pretty way to do everything, if you're not careful.
First off I think that's about the most concise way you can check for that particular condition.
Second, to me this is a code smell that indicates a potential flaw in your design. Generally nil and zero shouldn't mean the same thing. If possible you should try to eliminate the possibility of val being nil before you hit this code, either by checking that at the beginning of the method or some other mechanism.
You might have a perfectly legitimate reason to do this in which case I think your code is good, but I'd at least consider trying to get rid of the nil check if possible.
You can use the Object.nil? to test for nil specifically (and not get caught up between false and nil). You can monkey-patch a method into Object as well.
class Object
def nil_or_zero?
return (self.nil? or self == 0)
end
end
my_object = MyClass.new
my_object.nil_or_zero?
==> false
This is not recommended as changes to Object are difficult for coworkers to trace, and may make your code unpredictable to others.
nil.to_i returns zero, so I often do this:
val.to_i.zero?
However, you will get an exception if val is ever an object that does not respond_to #to_i.
I believe your code is incorrect; it will in fact test for three values: nil, false, and zero. This is because the !val expression is true for all values that are false, which in Ruby is nil and false.
The best I can come up with right now is
if val == nil || val == 0
# do stuff
end
Which of course is not very clever, but (very) clear.
My solution also use Refinements, minus the conditionals.
module Nothingness
refine Numeric do
alias_method :nothing?, :zero?
end
refine NilClass do
alias_method :nothing?, :nil?
end
end
using Nothingness
if val.nothing?
# Do something
end
Short and clear
[0, nil].include?(val)
Shortest and best way should be
if val&.>(0)
# do something
end
For val&.>(0)
it returns nil when val is nil since > basically is also a method, nil equal to false in ruby. It return false when val == 0.
Rails does this via attribute query methods, where in addition to false and nil, 0 and "" also evaluate to false.
if (model.attribute?) # => false if attribute is 0 and model is an ActiveRecord::Base derivation
However it has its share of detractors. http://www.joegrossberg.com/archives/002995.html
To be as idiomatic as possible, I'd suggest this.
if val.nil? or val == 0
# Do something
end
Because:
It uses the nil? method.
It uses the "or" operator, which is preferable to ||.
It doesn't use parentheses, which are not necessary in this case. Parentheses should only be used when they serve some purpose, such as overriding the precedence of certain operators.
I deal with this by defining an "is?" method, which I can then implement differently on various classes. So for Array, "is?" means "size>0"; for Fixnum it means "self != 0"; for String it means "self != ''". NilClass, of course, defines "is?" as just returning nil.
You can use case if you like:
case val with nil, 0
# do stuff
end
Then you can use anything that works with ===, which is nice sometimes. Or do something like this:
not_valid = nil, 0
case val1 with *not_valid
# do stuff
end
#do other stuff
case val2 with *not_valid, false #Test for values that is nil, 0 or false
# do other other stuff
end
It's not exactly good OOP, but it's very flexible and it works. My ifs usually end up as cases anyway.
Of course Enum.any?/Enum.include? kind of works too ... if you like to get really cryptic:
if [0, nil].include? val
#do stuff
end
The right thing to do is of course to define a method or function. Or, if you have to do the same thing with many values, use a combination of those nice iterators.
I really like Rails blank? method for that kind of things, but it won't return true for 0. So you can add your method:
def nil_zero?
if respond_to?(:zero?)
zero?
else
!self
end
end
And it will check if some value is nil or 0:
nil.nil_zero?
=> true
0.nil_zero?
=> true
10.nil_zero?
=> false
if val.nil_zero?
#...
end
Instead of monkey patching a class, you could use refinements starting in Ruby 2.1. Refinements are similar to monkey patching; in that, they allow you to modify the class, but the modification is limited to the scope you wish to use it in.
This is overkill if you want to do this check once, but if you are repeating yourself it's a great alternative to monkey patching.
module NilOrZero
refine Object do
def nil_or_zero?
nil? or zero?
end
end
end
using NilOrZero
class Car
def initialize(speed: 100)
puts speed.nil_or_zero?
end
end
car = Car.new # false
car = Car.new(speed: nil) # true
car = Car.new(speed: 0) # true
Refinements were changed in the last minute to be scoped to the file. So earlier examples may have shown this, which will not work.
class Car
using NilOrZero
end
This is very concise:
if (val || 0) == 0
# Is nil, false, or zero.
end
It works as long as you don't mind treating false the same as nil. In the projects I've worked on, that distinction only matters once in a while. The rest of the time I personally prefer to skip .nil? and have slightly shorter code.
[Update: I don't write this sort of thing any more. It works but is too cryptic. I have tried to set right my misdeeds by changing the few places where I did it.]
By the way, I didn't use .zero? since this raises an exception if val is, say, a string. But .zero? would be fine if you know that's not the case.
This evaluates to true for nil and zero: nil.to_s.to_d == 0
unless (val || 0).zero?
# do stufff
end
In a single stretch you can do this:
[do_something] if val.to_i == 0
nil.to_i will return 0
Another solution:
if val.to_i == 0
# do stuff
end
val ||= 0
if val == 0
# do something here
end

Resources