Ruby ternary operator without else - ruby

Is there a ruby idiom for "If do-this," and "do-this" just as a simple command?
for example, I'm currently doing
object.method ? a.action : nil
to leave the else clause empty, but I feel like there's probably a more idiomatic way of doing this that doesn't involve having to specify a nil at the end. (and alternatively, I feel like taking up multiple lines of code would be wasteful in this case.

As a general rule: you pretty much never need the ternary operator in Ruby. The reason why you need it in C, is because in C if is a statement, so if you want to return a value you have to use the ternary operator, which is an expression.
In Ruby, everything is an expression, there are no statements, which makes the ternary operator pretty much superfluous. You can always replace
cond ? then_branch : else_branch
with
if cond then then_branch else else_branch end
So, in your example:
object.method ? a.action : nil
is equivalent to
if object.method then a.action end
which as #Greg Campbell points out is in turn equivalent to the trailing if modifier form
a.action if object.method
Also, since the boolean operators in Ruby not just return true or false, but the value of the last evaluated expression, you can use them for control flow. This is an idiom imported from Perl, and would look like this:
object.method and a.action

a.action if object.method?

Greg's answer is the best, but for the record, and even more than in C, expressions and statements are equivalent in Ruby, so besides a.action if o.m? you can also do things like:
object.method? && a.action
You can write (a; b; c) if d or even
(a
b
c
) if d
or for that matter: (x; y; z) ? (a; b c) : (d; e; f)
There is no situation in Ruby where only a single statement or expression is allowed...

result = (<expression> && <true value>) || <false value>
value = 1
result = (value == 1 && 'one' ) || 'two'
result #=> 'one'
Explain: value == 1 && 'one' #=> returns last expression result, value is equals 1 so and section will be evaluated, and return 'one'.
value = 0
result = (value == 1 && 'one' ) || 'two'
result #=> 'two'
Explain: value != 1 and 'and' expression will not be evaluated, but instad will be used 'or' expression and it returns 'two'

Another way this can be done on the same line is:
if object.method; a.action end
This is considered bad style by Rubocop because it uses a semicolon to terminate the expression, but I find it more readable in some conditions than tacking on the if statement at the end. It is easier to overlook an if statement at the end and I don't always want to return something if the condition isn't true(as you are forced into with a ternary operator).
You can also be a bit more verbose and rubocop friendly:
if object.method then a.action end

Related

use variable in if statement operator in ruby

I have a variable that contains either '&&' or '||' and I need to use it in an if statement like so.
operator = '&&'
result = 1===1 operator 2===2
#=> result = 1===1 && 2===2
How can I achieve this?
I have tried using #public_send but to no avail.
Any help would be appreciated.
The && operator is more of a low-level syntax element than & and | which are method calls. That makes it harder to do.
However, there's two ways:
a = true
b = false
c = true
If you want && equivalence:
[ a, b, c ].all?
# => false
[ a, c ].all?
# => true
If you want || equivalence:
[ a, b, c ].any?
# => true
[ a, b ].any?
# => true
[ b ].any?
# => false
You could do it like this:
operator = "&&"
result = eval "1===1 #{operator} 2===2"
does that do what you want?
or if 1===1 is just a placeholder here for some expression, assign the result to a variable, so:
operator = "&&"
a = 1===1
b = 2===2
result = eval "#{a} #{operator} #{b}"
(of course, as commenters have noted, you must pay attention to the security vulnerabilities of eval, e.g. check that the operator variable is either "&&" or "||".)
and if you really want an if statement:
if operator == '&&'
1===1 && 2===2
elsif operator == '||'
1===1 || 2===2
else
# dunno
end
You can use public_send, but even if it were allowed with &&, it would not make sense. Note that E1 && E2 shortcircuits, in that E2 is not evaluated if E1 is already falsy. How could this work with public_send?
If you want to have this effect, you have to give up shortcircuiting, which means that you have to use & and | instead of && and ||. If you insist that your variable operator contains something like '&&', and not, say :& (which would make more sense), you could do a
result = (1===1).public_send(operator[0], 2===2)
to use your example for demonstration.
UPDATE: The comment of tadman to my answer made me think that I should warn you about the following trap: Based on your question, I assumed that the arguments you want to connect with your operator are either true or false. In this case, my solution indeed should work. However, if you they can be just any truey or falsy value of some class X, you need to convert them to true or false, because otherwise, the operator may have a different interpretation (for instance, if you operands happen to be integer). Hence, for some general operand_e1_ and e2, which - as is the general rule in Ruby - are supposed to be interpreted als false if it is either false or nil, and is taken as true otherwise, the safe way to calculate your result would be
result = (!!e1).public_send(operator[0], !!e2)
where !! ensures that the expressions are converted to true and false so that the operators & and | can safely be applied.

Logical OR order of operations/precedence not as expected

https://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Operators#Logical_Or
The binary "or" operator will return the logical disjunction of its
two operands. It is the same as "||" but with a lower precedence
As I understand it, as || has a higher precedence than "or", the code below will first test if 'b' is true, then 'c', and then if both are false will test 'a'. I would like to witness this but am not sure if my understanding is incorrect, or simply my test.
a = true
b = false
c = false
p a or b || c
==> true #but were b and c checked as expected?
My attempt to test this is as follows..
def atest
a = "string a"
a.include? 'string'
a
end
def btest
b = "string b"
b.include? 'string'
b
end
def ctest
c = "string c"
c.include? 'string'
c
end
puts "#{atest or btest || ctest}"
==> string a
I expected 'string b' to be returned... I'm completely new to programming since a week ago so I'm not sure, is my code wrong or is my understanding of the quote wrong?
edit: Appreciation on its own seems to be discouraged in responses, so I'll hide it here. Cheers for all the answers/further reading/code cleanup, it's clear now where I was going wrong.
In Ruby, &&, and, ||, and or are short-circuit operators:
[...] the second argument is executed or evaluated only if the first argument does not suffice to determine the value of the expression: when the first argument of the AND function evaluates to false, the overall value must be false; and when the first argument of the OR function evaluates to true, the overall value must be true.
In your example:
a or b || c
Ruby evaluates a, sees a truthy value and immediately returns it, without evaluating b || c.
or's lower precedence means that the expression is evaluated as:
a or (b || c)
instead of:
(a or b) || c
Just like
1 + 2 * 3
is evaluated as:
1 + (2 * 3)
because + has lower precedence than *.
But it doesn't change the order of evaluation. 1 is still evaluated before 2 * 3.
Also note that due to or's very low precedence
p a or b || c
is evaluated as:
(p a) or (b || c)
Neither b nor c will ever be printed this way.
Further readings:
Using “and” and “or” in Ruby
How to use Ruby’s English and/or operators without going nuts
Ruby: On The Perl Origins Of “&&” And “||” Versus “and” And “or”.
Sidenote: In your code there are lines that basically do nothing: a.include? 'string' is silently returning truthy, having no impact at all on the running code. These three functions might be rewritten as:
def atest
"string #{__callee__[0]}"
end
alias :btest :atest
alias :ctest :atest
You are confusing operator precedence with normal execution flow. Operator precedence is about what operation to execute in advance when we have two operations applied to the same operand. Otherwise, the execution order is left-to-right. E.g.
a + b * c
is executed as:
a
b
# oh! ambiguity: lookup the precedence table: will do multiplication
c # (otherwise a + b)
*
+

Does ruby need ternary operator at all?

I made a small test today:
> false && 1 || 2
> 2
> true && 1 || 2
> 1
So if we could already do with binary operators, why did we need a ternary ?
> false ? 1 : 2
> 2
> true ? 1 : 2
> 1
As it is not simply an alias and complicates parsing.
To deal with the specific case in your question…
What if 1 was different value, one that evaluated as false?
And in general:
No. You can always replace a ternary operator with an if/else construct. That isn't as convenient though.
No language needs a ternary operator.
The ternary operator is a well-known language construct. IMO people generally expect it in script-ish(-looking) languages, so there it is. I don't see a huge reason to not have it.
The conditional operator is needed in languages like C, because if/else is a statement, it doesn't evaluate to a return value. But in Ruby, everything is an expression, everything has a return value, there are no statements.
Therefore, you can always replace the conditional operator with a conditional expression:
foo = bar ? baz : qux
is exactly equivalent to
foo = if bar then baz else qux end
In C, you cannot write this, you'd have to write
if bar then foo = baz else foo = aux end
leading to code duplication. That's why you need the conditional operator in C. In Ruby, it is unnecessary.
Actually, since Ruby is an object-oriented language, all conditionals are unnecessary. You can just use polymorphism instead.

|| 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.

What is the difference between "if" statements with "then" at the end?

What's the difference between these two Ruby if statements when we put a then at the end of the if statement?
if(val == "hi") then
something.meth("hello")
else
something.meth("right")
end
and
if(val == "hi")
something.meth("hello")
else
something.meth("right")
end
then is a delimiter to help Ruby identify the condition and the true-part of the expression.
if condition then true-part else false-part end
then is optional unless you want to write an if expression in one line. For an if-else-end spanning multiple lines the newline acts as a delimiter to split the conditional from the true-part
# can't use newline as delimiter, need keywords
puts if (val == 1) then '1' else 'Not 1' end
# can use newline as delimiter
puts if (val == 1)
'1'
else
'Not 1'
end
Here's a quick tip that is not directly related to your question: in Ruby, there is no such thing as an if statement. In fact, in Ruby, there are no statements at all. Everything is an expression. An if expression returns the value of the last expression that was evaluated in the branch that was taken.
So, there is no need to write
if condition
foo(something)
else
foo(something_else)
end
This would better be written as
foo(
if condition
something
else
something_else
end
)
Or as a one-liner
foo(if condition then something else something_else end)
In your example:
something.meth(if val == 'hi' then 'hello' else 'right' end)
Note: Ruby also has a ternary operator (condition ? then_branch : else_branch) but that is completely unnecessary and should be avoided. The only reason why the ternary operator is needed in languages like C is because in C if is a statement and thus cannot return a value. You need the ternary operator, because it is an expression and is the only way to return a value from a conditional. But in Ruby, if is already an expression, so there is really no need for a ternary operator.
The then is only required if you want to write the if expression on one line:
if val == "hi" then something.meth("hello")
else something.meth("right")
end
That brackets in your example are not significant, you can skip them in either case.
See the Pickaxe Book for details.
The only time that I like to use then on a multi-line if/else (yes, I know it's not required) is when there are multiple conditions for the if, like so:
if some_thing? ||
(some_other_thing? && this_thing_too?) ||
or_even_this_thing_right_here?
then
some_record.do_something_awesome!
end
I find it to be much more readable than either of these (completely valid) options:
if some_thing? || (some_other_thing? && this_thing_too?) || or_even_this_thing_right_here?
some_record.do_something_awesome!
end
# or
if some_thing? ||
(some_other_thing? && this_thing_too?) ||
or_even_this_thing_right_here?
some_record.do_something_awesome!
end
Because it provides a visual delineation between the condition(s) of the if and the block to execute if the condition(s) evaluates to true.
There's no difference at all.
And, just FYI, your code can be optimized to
something.meth( val == 'hi' ? 'hello' : 'right' )

Resources