I was going through some code I found online and found the following
def change input
('a'..'z').map { |letter| input.downcase.include?(letter) ? '1' : '0' }.join
end
I understand what this code is doing. It will take a string, check if the string contains each letter of the alphabet and return 1 if true and 0 if false.
However I am unfamiliar with this bit of syntax:
?(letter) ? '1' : '0' }
I know that a question mark is usually used to indicate that the method will return a boolean result. But I am insure why there is a second question mark after the argument.
Also, I understand that this will return 1 if true and 0 if false. Is that what this colon represents. Is it always ok to use a colon like this if the result of the method in the block will be a boolean?
The format boolean_expression ? option_a : option_b is called a ternary operator. It is short for
if boolean_expression
option_a
else
option_b
end
The first question mark is part of the #include? method
The expession condition ? if_true : if_false is called a ternary operator, which is shorthand for
if condition
if_true
else
if_false
end
Related
I'm having a little difficulty in understanding the below under initialize, and color methods. This is from the solution I was given:
The one under initialize:
I think it means #given equals value, and if value is equal to 0, then its false, otherwise its true
Under color not sure what the 2nd question mark means.
def initialize(value)
#value = value
#given = value == 0 ? false : true
end
def color
given? ? :blue : :red
end
def given?
#given
end
Under color not sure what the 2nd question mark means.
This is called the conditional operator. (See, for example, section 11.5.2.2.5 Conditional operator expression of the ISO Ruby Language Specification.)
condition ? consequence_truthy : consequence_falsy
will evaluate condition and depending on whether the result is truthy or falsy, it will evaluate either the first or the second consequence.
Personally, I find the conditional operator useless in Ruby. In C and similar languages, the conditional operator is required because it is an expression, whereas the conditional statement is, well, a statement. So, if you need to use a conditional in an expression, you cannot use a conditional statement, you must use the conditional operator.
However, this does not apply to Ruby: in Ruby, everything is already an expression, there are no statements. The conditional expressions (see, for example, section 11.5.2.2.2 The if expression, section 11.5.2.2.3 The unless expression, and section 11.5.2.2.4 The case expression) can already be used as an expression, so there is no need to use the conditional operator.
Personally, I find the conditional expression more readable than the conditional operator:
if condition then consequence_truthy else consequence_falsy end
This is semantically equivalent to the conditional operator, the only difference is syntactical: it has different precedence, which however, actually works more natural than the precedence of the conditional operator.
So, your examples could, in my opinion, be clearer written as
def initialize(value)
#value = value
#given = if value == 0 then false else true end
end
def color
if given? then :blue else :red end
end
Actually, the initialize contains a useless-use-of-conditional because Integer#== already returns a boolean. So, the following would be even clearer:
def initialize(value)
#value = value
#given = value != 0
end
Mostly we use ? mark as a part of the method's name which returns boolean value. this is just a convention we ruby coders use to follow.
In your example given? is the method name as it return boolean value so here we have follow the convention.
and in ternary conditional operator we use
condition ? do something when condition is true : do something when condition is false
In your case
given? it's returning true or false and which is also method name
So the first question mark is a part of the method name and 2nd question mark is the part of ternary condition
I'm fairly new to code and I have a quick question on Ruby conditionals, specifically Case Expressions. I have a method in which I want to return a string "odd" if the string length is odd and "even" if the string length is even.
Simple stuff I know and I can get the results using a if/else conditional, however the case expression just returns 'nil'. Any help would be greatly appreciated.
def odd_or_even(string)
case string
when string.length.even? then "even"
when string.length.odd? then "odd"
end
end
odd_or_even("Ruby") # Wanting to return even rather than nil
odd_or_even("Rails") # Wanting to return odd rather than nil
You've written your case statement wrong. It takes two forms, which is unusual compared to other languages. The first form takes an argument, and that argument is compared to all possible cases. The second form is without argument and each case is evaluated independently.
The most minimal fix is this:
def odd_or_even(string)
case
when string.length.even? then "even"
when string.length.odd? then "odd"
end
end
This was because to Ruby your code looked like this when calling with the argument "Ruby":
def odd_or_even(string)
case string
when true then "even"
when false then "odd"
end
end
Your string value does not match true or false so you get nil from a non-matching situation.
You can clean up your code considerably. Consider: Can something be not even and not odd? Not really:
def odd_or_even(string)
string.length.even? ? 'even' : 'odd'
end
case something
when condition1
expression1
when condition2
expression2
else
default_expression
end
is equivalent to
if condition1 === something
expression1
elsif condition2 === something
expression2
else
default_expression
end
Note that case-when internally uses ===, an operator (method) which can be overridden.
Experimenting with the conditional operator in ruby,
def nada
false ? true : nil
end
def err
false ? true : raise('false')
end
work as expected but
def reflection
false ? true : return false
end
produces a syntax error, unexpected keyword_false, expecting keyword_end
def reflection
false ? true : return(false)
end
and attempted with brackets syntax error, unexpected tLPAREN, expecting keyword_end
yet
def reflection
false ? true : (return false)
end
works as expected, and the more verbose if...then...else...end
def falsy
if false then true else return false end
end
also works as expected.
So what's up with the conditional (ternary) operator?
You can use it like this, by putting the entire return expression in parentheses:
def reflection
false ? true : (return false)
end
Of course, it does not make much sense used like this, but since you're experimenting (good!), the above works! The error is because of the way the Ruby grammar works I suppose - it expects a certain structure to form a valid expression.
UPDATE
Quoting some information from a draft specification:
An expression is a program construct which make up a statement (see 12
). A single expression can be a statement as an expression-statement
(see 12.2).12
NOTE A diļ¬erence between an expression and a statement is that an
expression is ordinarily used where its value is required, but a
statement is ordinarily used where its value is not necessarily
required. However, there are some exceptions. For example, a
jump-expression (see 11.5.2.4) does not have a value, and the value
of the last statement of a compound-statement can be used.
NB. In the above, jump-expression includes return among others.
I think this is all related to the ruby parser.
ruby parses return as the else-expression of the ternary operator
ruby is then surprised when it finds false instead of end
wrapping return false in parentheses causes ruby to interpret the entire thing as the else-expression
return(false) doesn't work because ruby is still trying to interpret just the return part as the else-expression, and is surprised when it finds a left-parenthesis (updated)
Note: I don't think this is a great answer.
A great answer could, for example, explain the parse errors with reference to the ruby grammar.
The ternery operator is just that, an operator. You don't return from it. You return from functions. When you put a return in an if, you return from the function that the if is in. Since there is no variable awaiting assignment from the result of the if, there is no problem. When you return from the ternery operator, there is no value assigned to the variable.
I'm a newbie to Ruby, I have a problem following the Poignant Guide to Ruby:
Does this expression return true?
2005..2009 === 2007
But I don't know why I got this warning message from the following code
wishTraditional.rb:4: warning: integer literal in conditional range
code:
def makTimeLine(year)
if 1984 === year
"Born."
elsif 2005..2009 === year
"Sias."
else
"Sleeping"
end
end
puts makTimeLine(2007)
and the it return Sleeping, which is wrong and should be the Sias
BTW what does the two dots mean? How can I find more information about it?
I think you better use something like that :
elsif (2005..2009).include?(year)
Here is the documentation about Ruby ranges
Update: if you insist on using ===, you should enclose the range in parentheses:
elseif (2005..2009) === year
For independent expressions, yes, you'll need to put range literals in parentheses.
But your if/elsif chain would be cleaner as a case statement, which uses === for comparison:
def makTimeLine(year)
case year
when 1984
"Born."
when 2005..2009
"Sias."
else
"Sleeping"
end
end
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