In many ways, a computer program is like a journey for your data. Along this journey, data encounters many things that have an impact on it and it is forever changed. Like any journey, one must travel a given path. On that path, there are many roads. Some roads are chosen and others not. Which roads are chosen depends on the end goal.
When you are writing programs, you want your data to make the right decisions. You want your data to do the right thing when it's supposed to. In computer programming, this is called conditional flow.
How do we make data do the right thing? We use conditionals.
A conditional is a fork (or many forks) in the road. Your data approaches a conditional and the conditional then tells the data where to go based on some defined parameters. Conditionals are formed using a combination of if
statements and comparison and logical operators (<, >, <=, >=, ==, !=, &&, ||)
. They are basic logical structures that are defined with the reserved words if
, else
, elsif
, and end
. Note that elsif
is missing an "e". Enough talking, time to code.
Create a file called conditional.rb
and type the following code into it.
# conditional.rb
puts "Put in a number"
a = gets.chomp.to_i
if a == 3
puts "a is 3"
elsif a == 4
puts "a is 4"
else
puts "a is neither 3, nor 4"
end
Here we are using gets
to let the user input a number, chomp
gets rid of the new line created when the user enters the data, and to_i
is a method that can be called on a string to turn it into an integer. We need to convert the input into an integer because gets
always gives us a string.
Run this code three times and do the following:
You can repeat the third step more than once to see its effect.
What your code is doing is checking, using the ==
operator you learned previously, to see if the input is equal to the number we have defined. We have effectively controlled the flow of the program by setting conditionals in an if
statement. Nice work!
The examples below are all valid Ruby conditionals.
# Example 1
if x == 3
puts "x is 3"
end
# Example 2
if x == 3
puts "x is 3"
elsif x == 4
puts "x is 4"
end
# Example 3
if x == 3
puts "x is 3"
else
puts "x is NOT 3"
end
# Example 4: must use "then" keyword when using 1-line syntax
if x == 3 then puts "x is 3" end
Last, because Ruby is such an expressive language, it also allows you to append the if
condition at the very end. Example 1 from above could be rewritten like this:
puts "x is 3" if x == 3
Ruby also has a reserved word, unless
. It acts as the opposite of if
, so you can use it like this:
puts "x is NOT 3" unless x == 3
Let's go over these comparison operators in a little more depth so you can build some more complicated conditional statements. One thing to remember is that comparison operators always return a boolean value. A boolean value is either true
or false
, nothing else. We'll try them out in irb to see how they work as well.
The expressions or values that an operator uses are its operands. In comparisons, the expressions to the left and right of the operator are the operands.
==
- The "is equal to" operator. Anything to the left of the symbol is exactly equal to anything on the right. We talked about this operator earlier in our chapter on variables so it shouldn't be totally foreign.
irb :001 > 5 == 5
=> true
irb :002 > 5 == 6
=> false
irb :003 > 'abc' == 'abc'
=> true
irb :004 > 'abc' == 'abcd'
=> false
irb :005 > 'abc' == 'aBc'
=> false
irb :006 > '5' == '5'
=> true
irb :007 > '5' == '6'
=> false
irb :008 > '5' == 5
=> false
Notice that we can compare strings for equality. To be equal, two strings must have the exact same value. The specific value doesn't matter, just that both strings have the same value. If there is any difference at all, the strings are not equal.
We threw that last example in as a reminder that two values must have the same type or they are not equal. Thus, the string '5'
is not the same as the number 5
. The comparison is thus false
.
!=
- The "not equal to" operator. Anything to the left of the symbol is not equal to anything to the right.
irb :001 > 4 != 5
=> true
irb :002 > 4 != 4
=> false
irb :003 > 4 != 156
=> true
irb :004 > 'abc' != 'def'
=> true
irb :005 > 'abc' != 'aBc'
=> true
irb :006 > '5' != 5
=> true
As with ==
, we can easily compare two values of the same type for inequality and get reasonable results. However, if the two values have different types, the return value is true
.
<
- The "less than" symbol. Anything to the left of the symbol has a lower value than anything to the right of the symbol.
>
- The "greater than" symbol. Anything to the left of the symbol has a higher value than anything to the right of the symbol.
# Example using 'less than' and 'greater than'
irb :001 > 4 < 5
=> true
irb :002 > 4 > 5
=> false
irb :003 > "4" < "5"
=> true
irb :004 > "4" > "5"
=> false
irb :005 > "42" < "402"
=> false
irb :006 > "42" > "402"
=> true
irb :007 > "42" < "420"
=> true
irb :008 > "42" < 420
ArgumentError (comparison of String with 420 failed)
irb :009 > 42 > "420"
ArgumentError (comparison of Integer with String failed)
The examples numbered 005
, 006
, and 007
are especially tricky! Make sure you understand them. When comparing strings, the comparison is character-by-character. Ruby moves from left-to-right in the strings looking for the first character that is different from its counterpart in the other string. Once it finds a character that differs, it compares that character with its counterpart, and makes a decision based on that. If both strings are equal up to the length of the shorter string as in the final example, then the shorter string is considered less than the longer string.
The final two examples show that you can not use <
and >
with values of different types. (The same holds true for the <=
and >=
operators shown below.)
<=
- The "less than or equal to" symbol. Anything to the left of the symbol is less than or equal to anything on the right.
>=
- the "greater than or equal to" symbol. Anything to the left of the symbol is greater than or equal to anything on the right.
irb :001 > 4 <= 5
=> true
irb :002 > 5 >= 5
=> true
irb :003 > 4 >= 5
=> false
irb :004 > 4 >= 3
=> true
irb :005 > 4 >= 4
=> true
Of course, the <=
and >=
operators work equally well with strings.
OK, you're starting to get a decent grasp of conditional flow. It is also possible to combine multiple conditional expressions together to create a more specific scenario. We can do this using the &&
and ||
operators. Let's see what they mean.
&&
- the "and" operator. Expressions to the left and to the right of this operator both have to be true for the entire expression to be evaluated to true.
irb :001 > (4 == 4) && (5 == 5)
=> true
irb :002 > (4 == 5) && (5 == 5)
=> false
irb :002 > (4 == 5) && (5 == 6)
=> false
||
- the "or" operator. Either the expression to the left has to be true, or the expression to the right has to be true for the entire expression to be evaluated to true.
irb :001 > (4 == 4) || (5 == 5)
=> true
irb :002 > (4 == 5) || (5 == 5)
=> true
irb :002 > (4 == 5) || (5 == 6)
=> false
!
- the "not" operator. When you add this in front of a boolean expression it will change that boolean value to its opposite.
irb :001 > !(4 == 4)
=> false
What happens here is Ruby first evaluates what is in the parentheses and then the !
operator changes it. We know that 4 == 4
would return true. If we say !true
then that returns false. You can think of !true
as saying "not true".
&&
and ||
don't always return true
or false
, but they do when they operate on boolean values. A little later in this chapter we'll see what happens when we use &&
and ||
with non-boolean values.
Note: When you are combining expressions as we are above, it is helpful to use parentheses to group expressions together. This is helpful for readability and also helps the computer more accurately understand your intention. The computer will evaluate parentheses in normal algebraic order.
Ruby follows an order of precedence when deciding how to evaluate multiple expressions. The following is a list of operations from highest order of precedence (top) to lowest (bottom).
<=
, <
, >
, >=
- Comparison
==
, !=
- Equality
&&
- Logical AND
||
- Logical OR
Knowing this, we can look at the following expression and see how it is evaluated.
if x && y || z
# do something
end
First the x && y
statement will be executed. If that statement is true, then the program will execute the # do something
code on the next line. If the x && y
statement is false, then the z
will be evaluated. If the z
is true, the code on the next line will be evaluated. If the z
is false, then the code will exit the if
statement.
Ruby has a nice option for short and concise conditional if
statements. The ternary operator is a common Ruby idiom that makes a quick if/else
statement easy and keeps it all on one line.
The ternary operator uses a combination of the ?
and :
.
# Ternary operator example
irb :001 > true ? "this is true" : "this is not true"
=> "this is true"
irb :001 > false ? "this is true" : "this is not true"
=> "this is not true"
How does this work? You may have inferred that first the computer evaluates what is to the left of the ?
. If the expression to the left of ?
evaluates as true, the code directly to the right of the ?
gets executed. If the code on the left of the ?
evaluates as false, then the code directly to the right of the :
gets executed.
Ternary operators definitely come in handy as you start to get more familiar with if
statements. If you feel like you are unsure of how this works, play around with it in irb and test some other cases out. Nothing can create familiarity more quickly than good ol' repeated exposure and experimentation.
Ternary expressions should usually be used to select between 2 values, not to choose between two actions. (An action would be something like printing a value or setting a variable to a new value.) The ternary expression's result should almost always be assigned to a variable, passed to a method as an argument, or returned by a method. If you're not doing one of those things, an if/else
statement is a better choice.
For example, all of the following are good examples of using a ternary expression:
foo = hitchhiker ? 42 : 3.1415 # Assign result of ?: to a variable
puts(hitchhiker ? 42 : 3.1415) # Pass result as argument
return hitchhiker ? 42 : 3.1415 # Return result
However, the following snippets use ternaries that choose between actions, and should be considered inappropriate uses:
hitchhiker ? (foo = 42) : (bar = 3.1415) # Setting variables
hitchhiker ? puts(42) : puts(3.1415) # Printing
In general, all components of a ternary expression should be relatively simple expressions. Aim for readability, not brevity.
The final conditional flow structure that we want to talk about is called a case statement. A case statement has similar functionality to an if
statement but with a slightly different interface.
Case statements use the reserved words case
, when
, else
, and end
. You create one by first defining a case and then evaluating the value of the case and what operation to complete if that case is true. As always, talking about this stuff is much harder than simply observing how the code behaves. Let's create a file called case_statement.rb
to play with some case statements and see how they work.
# case_statement.rb
a = 5
case a
when 5
puts "a is 5"
when 6
puts "a is 6"
else
puts "a is neither 5, nor 6"
end
This example is roughly equivalent to the following if/elsif/else
statement:
# if_statement.rb
a = 5
if a == 5
puts "a is 5"
elsif a == 6
puts "a is 6"
else
puts "a is neither 5, nor 6"
end
The chief differences are that we only need to specify the variable we want to test once (as the argument to case
) and we don't specify a ==
on the individual when
statements.
You can also save the result of a case statement into a variable. Let's refactor the code above to do just that. This way we don't have to write puts
so many times.
# case_statement.rb <-- refactored
a = 5
answer = case a
when 5
"a is 5"
when 6
"a is 6"
else
"a is neither 5, nor 6"
end
puts answer
There's a second form of the case
statement that doesn't take an argument:
# case_with_no_arg_statement.rb
a = 5
case
when a == 5
puts "a is 5"
when a == 6
puts "a is 6"
else
puts "a is neither 5, nor 6"
end
The difference here is that we don't provide an argument on line 5, and we have to fully test each value with a ==
; most developers prefer to use if/elsif/else/end
instead, but there are situations where this form is preferred. We won't get into that here.
As you can see, there are lots of uses for case
statements and they can be very powerful tools when writing Ruby programs. Remember, if you're uncomfortable with these, spend some time modifying them and watching how they respond to the changes you make. Test their boundaries to see what they are capable of. Curiosity will serve you well in your journey to learning Ruby. There is much to discover!
Notice that after if
and elsif
we have to put an expression that evaluates as either true or false. In Ruby, you could even write code like this:
a = 5
if a
puts "how can this be true?"
else
puts "it is not true"
end
The output is "how can this be true?". In Ruby, every expression evaluates as true when used in flow control, except for false
and nil
. Try the code above and give a
values of 0, ""(empty string) and even the string 'false' to see the result yourself!
Because of this, we could even write code like this:
if x = 5
puts "how can this be true?"
else
puts "it is not true"
end
The above code is not testing whether x
is equal to "5". It's assigning the variable x
the value of "5", which will always evaluate to true
. Unfortunately, that looks very similar to if x == 5
, which is testing whether x
is equal to "5". Be careful when reading or writing Ruby; its expressiveness can also be a source of many subtle bugs.
The &&
and ||
logical operators, as you'll recall, use short-circuit evaluation. These operators work with truthy and falsy values too, and they can also return truthy values instead of boolean values. When using &&
and ||
, the return value is always the value of the operand evaluated last:
irb :001 > 3 || 'foo' # last evaluated operand is 3
=> 3
irb :002 > 'foo' || 3 # last evaluated operand is 'foo'
=> 'foo'
irb :003 > nil || 'foo' # last evaluated operand is 'foo'
=> 'foo'
irb :004 > nil && 'foo' # last evaluated operand is nil
=> nil
irb :005 > 3 && 'foo' # last evaluated operand is 'foo'
=> 'foo'
irb :006 > 'foo' && 3 # last evaluated operand is 3
=> 3
Suppose you have an expression of some kind that returns a value that is either truthy or falsy, but isn't a boolean value:
foo = nil
bar = 'qux'
is_ok = foo || bar
In this code, is_ok
gets set to a truthy value of "qux"
. In most cases, you can use "qux"
as though it were actually a boolean true
value. However, using a string value as though it is a boolean isn't the clearest way to write your code. It may even look like a mistake to another programmer who is trying to track down a bug. In some strange cases, it may even be a mistake.
You can address this easily enough by using an if
statement or a ternary expression:
is_ok = (foo || bar) ? true : false
is_ok
if (foo || bar)
is_ok = true;
else
is_ok = false;
end
Either of those snippets sets is_ok
to an appropriate boolean value. However, they do so in a somewhat wordy manner. Many JavaScript programmers use a more concise coercion by using what looks like a !!
operator:
is_ok = !!(foo || bar)
In reality, !!
isn't a separate operator in Ruby Instead, it's two consecutive !
operators. The expression !!a
is equivalent to writing !(!a)
. The inner !
converts the value of a
to false
if it is truthy, or true
if a
is falsy. The outer !
then flips true
to false
or false
to true
. In the end, we end up with a boolean value instead of a truthiness value:
irb :001 !!3 # 3 is truthy, !3 is false, !false is true
=> true
irb :002 !!nil # nil is falsy, !nil is true, !true is false
=> false
This chapter covered booleans, comparisons and the ability to control the flow of code execution with conditionals. These are some of the fundamental tools that you'll carry with you as a Ruby developer. We've got more exercises to drill these skills into your head and fingers!
Write down whether the following expressions return true
or false
. Then type the expressions into irb
to see the results.
(32 * 4) >= 129
false != !true
true == 4
false == (847 == '847')
(!true || (!(100 / 5) == 20) || ((328 / 4) == 82)) || false
Video Walkthrough
Write a method that takes a string as an argument. The method should return a new, all-caps version of the string, only if the string is longer than 10 characters. Example: change "hello world" to "HELLO WORLD". (Hint: Ruby's String class has a few methods that would be helpful. Check the Ruby Docs!)
# caps_method.rb
def caps(string)
if string.length > 10
string.upcase
else
string
end
end
puts caps("Joe Smith")
puts caps("Joe Robertson")
Video Walkthrough
Write a program that takes a number from the user between 0 and 100 and reports back whether the number is between 0 and 50, 51 and 100, or above 100.
# evaluate_num.rb
puts "Please enter a number between 0 and 100."
number = gets.chomp.to_i
if number < 0
puts "You can't enter a negative number!"
elsif number <= 50
puts "#{number} is between 0 and 50"
elsif number <= 100
puts "#{number} is between 51 and 100"
else
puts "#{number} is above 100"
end
Video Walkthrough
What will each block of code below print to the screen? Write your answer on a piece of paper or in a text editor and then run each block of code to see if you were correct.
# Snippet 1
'4' == 4 ? puts("TRUE") : puts("FALSE")
# Snippet 2
x = 2
if ((x * 3) / 2) == (4 + 4 - x - 3)
puts "Did you get it right?"
else
puts "Did you?"
end
# Snippet 3
y = 9
x = 10
if (x + 1) <= (y)
puts "Alright."
elsif (x + 1) >= (y)
puts "Alright now!"
elsif (y + 1) == x
puts "ALRIGHT NOW!"
else
puts "Alrighty!"
end
"FALSE"
"Did you get it right?"
"Alright now!"
Video Walkthrough
When you run the following code...
def equal_to_four(x)
if x == 4
puts "yup"
else
puts "nope"
end
equal_to_four(5)
You get the following error message..
exercise.rb:8: syntax error, unexpected end-of-input, expecting keyword_end
Why do you get this error and how can you fix it?
You get this error because the end
in the code above gets matched with the if..else
statement. The error message is telling us that the interpreter was expecting the keyword end
to close off our equal_to_four
method, that end
was not found.
Video Walkthrough
Write down whether the following expressions return true
or false
or raise an error. Then, type the expressions into irb
to see the results.
(32 * 4) >= "129"
847 == '847'
'847' < '846'
'847' > '846'
'847' > '8478'
'847' < '8478'