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
Related
I'm wondering if there is a better way to handle multiple matches on longer method calls than repeating the long call in a piped statement. Example:
if value == ''
return nil
elsif class.method[:key].select(id: id).first.nil? || class.method[:key].select(id: id).first == 1
return nil
end
Ideally I'd like to one line this since it returns the same result, so the if/elsif is useless, but I'm not sure if I can match .nil? and == 1 on the same thing without repeating it or setting it as a variable beforehand.
Assuming your code otherwise works as posted, you can do what you want by storing the result of the chained method in a variable before performing your comparisons. For example:
return nil if value.empty?
result = class.method[:key].select(id: id).first
return nil if result.nil? || result.eql?(1)
The semantics here will do what you expect without having to repeat the method chain. There are certainly other ways to express the idea, such as a case statement, but this should do what you expect in a very compact way.
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.
I have a method that includes (among subsequent lines) the following code:
candidate_lines.each do |line|
return line if priority_1?(line) && line.marked_by?(self)
end
The methods in it, as far as I can tell, are irrelevant. What's driving me up the wall, is I have a test that should be skipping that part of the method simply by the specification I've given:
it "without any priority 1 lines, returns all priority 2 lines marked by itself" do
line1 = double :line, marked_by?: true
line2 = double :line, marked_by?: true
allow(joshua).to receive(:priority_1?).and_return(:false)
expect(joshua).to receive(:candidate_lines).and_return([line1, line2])
expect(joshua.tiebreak_lines).to eq [line1, line2]
end
Yet when I run the test, the return statement is getting triggered. I've tested it manually with puts statements immediately before the 'return line...' line, and it turns out that, while 'priority_1?(line)' returns false, for some reason 'priority_1?(line) && line.marked_by?(self)' is evaluating to true.
What's happening here?
You're confusing the symbol :false with the boolean value false
Because :false is neither false nor nil, it is truthy, that is as far as things like && or if are concerned, it is true.
def foo
true && return false
end
def bar
true and return false
end
foo method causes a syntax error. bar doesn't. Why?
Assuming I want to evaluate one-liners with a return statement (similar to a certain commonly used shell/bash expression), would this be a recommended way to do it, or is there a more recommended approach?
By operator associativity strength,
true && return false
evaluates to
(true && return) false
of which
true && return
is valid, but the false after it is invalid. You cannot have two statements lined up without anything in between.
Side Note
It is worth noting that and and && are not equivalent.
and is a flow control operator while && is a Boolean operator. What is the difference?
One example of the differences, using and, you can assign values.
value = nil and value / 42
This would fail if you tried it as below
value = nil && value / 42
Original Question 1
Assuming I want to evaluate one-liners with a return statement (similar to a certain > commonly used shell/bash expression), would this be a recommended way to do it, or is there > a more recommended approach?
The way I have always seen this done in Ruby is this:
value if conditional
This will return the value if the conditional is met and nil otherwise. No need for the return statement if this is the last command in the function!
If you are using this for an early exit to a function, I prefer using an unless. For instance...
return unless arguments_are_valid
Original Question 2
foo method causes a syntax error. bar doesn't. Why?
It's a matter of operator precedence. See the example below showing how they are evaluated.
(true && return) false
(true) and (return false)
Because of the && operator precedence, the following line
true && return false
evaluates as
(true && return) false
that does not makes sense. In order to execute your code you need to use
def foo
true && (return false)
end
and doesn't suffer of the same issue because and has lower precedence than &&.
if there is need for shorten statements use
def foo
return false if true
end
def bar
return false if true
end
return is not a function :) therefore it doesn't make sense to say
when true and return is ok send a false
I am trying to halt input from a user when their input is 42.
The correct answer on the website I'm working on is:
while line = gets
break if (/42/ =~ line)
x << line
end
The code I tried that does not work is:
while line = gets.chomp
break if (line == 42)
x << line
end
Why is this the case? Am I missing some limitations to what I can use in my if statement?
The problem is that 42 is an integer, but line is a string:
1.9.3p392 :001 > "42" == 42
=> false
So it's never the case that your if statement is getting triggered, because it's comparing two different kinds of things. Matching with a Regex fixes it, though it's looking for "42" to appear anywhere in the input (e.g. "3427"). I think what you meant to say was
while line = gets.chomp
break if (line == "42")
x << lineĀ
end
In other words, break when the input is a string with the characters 4 and 2 in it.
I suspect it's because you're comparing a number to a string. The example uses a regular expression it appears. "42" == 42 will give you false in ruby.
<< is a method(Append) on Array or String class objects. But your x not holding any referencing to such objects. Thus you are getting undefined local variable or method 'x' for main:Object (NameError).
Try this instead(by fixing local variable x to hold a practical object and converting line value to Fixnum object:
x = "hi"
while line = gets.chomp
break if (line.to_i == 42)
x << line
end
This program will help you to halt input from a user when their input is 42.
until (line = gets.chomp).to_i == 42
x << line
end
This of course bypasses the if statement you were asking about.
Your limitation for the if is based solely on the fact that you are comparing a string that will always be a string to a number, and this will never be equal. (as others have mentioned)
So we must reconsider the conditional statement. In this case, I considered it "out of place" and moved the comparison to the 'while' loop, and then inverted it to an 'until' statement, to be able to positively express the condition to end the loop on. Whenever I see a 'break' in a loop, I try to get rid of that smell, as the condition to leave a loop should really be expressed in the loop condition if possible.
I hope this helps.