Let’s take another look at our first branching program, here. What if my wife came home, saw the program, tried it, and it didn’t tell her what a lovely name she had? I wouldn’t want her to flip out, so let’s rewrite it:
puts 'Hello, what's your name?' |
name = gets.chomp |
puts 'Hello, ' + name + '.' |
|
if name == 'Chris' |
puts 'What a lovely name!' |
else |
if name == 'Katy' |
puts 'What a lovely name!' |
end |
end |
<= | Hello, what's your name? |
=> | Katy |
<= | Hello, Katy. |
What a lovely name! |
Well, it works…but it isn’t a very pretty program. Why not? It just doesn’t feel right to me that the whole “Katy” chunk of code is not lined up with the “Chris” chunk of code. These are supposed to be totally equal and symmetrical options, yet one feels distinctly subordinate to the other. (In fact, this code would probably get me sleeping on the couch faster than just leaving her out of the program altogether.) This code just isn’t jiving with my mental model.
Fortunately, another Ruby construct can help: elsif. This code means the same thing as the last program but feels so much lovelier:
puts 'Hello, what's your name?' |
name = gets.chomp |
puts 'Hello, ' + name + '.' |
|
if name == 'Chris' |
puts 'What a lovely name!' |
elsif name == 'Katy' |
puts 'What a lovely name!' |
end |
<= | Hello, what's your name? |
=> | Katy |
<= | Hello, Katy. |
What a lovely name! |
This is a definite improvement, but something is still wrong. If I want the program to do the same thing when it gets Chris or Katy, then it should really do the same thing, as in execute the same code. Here we have two different lines of code doing the same thing. That’s not right. That’s not how I’m thinking about this.
More pragmatically, it’s just a bad idea to duplicate code anywhere. Remember the DRY rule? Don’t Repeat Yourself! For pragmatic reasons, for aesthetic reasons, or just because you’re lazy, don’t ever repeat yourself! Weed out duplication in code (or even design) whenever you see it. In our case, we repeated the line puts 'What a lovely name!'. What we’re trying to say is just, “If the name is Chris or Katy, do this.” Let’s just code it that way:
puts 'Hello, what's your name?' |
name = gets.chomp |
puts 'Hello, ' + name + '.' |
|
if name == 'Chris' || name == 'Katy' |
puts 'What a lovely name!' |
end |
<= | Hello, what's your name? |
=> | Katy |
<= | Hello, Katy. |
What a lovely name! |
Nice. Much, much better. And it’s even shorter! I don’t know about you, but I’m excited. It’s almost the same as the original program! Bliss, I tell you…sparkly programming bliss!
To make it work, I used ||, which is how we say “or” in most programming languages.
At this point, you might be wondering why we couldn’t just say this:
... |
|
if name == ('Chris' || 'Katy') |
puts 'What a lovely name!' |
end |
It makes sense in English, but you have to remember how staggeringly brilliant humans are compared to computers. The reason this makes sense in English is that humans are just fabulous at dealing with context. In this context, it’s clear to a human that “if your name is Chris or Katy” means “if your name is Chris or if it is Katy.” (I even used “it”—another triumph of human context handling.) But when your computer sees ('Chris' || 'Katy'), it’s not even looking at the name == code; before it gets there, it just tries to figure out whether one of 'Chris' or 'Katy' is true…because that’s what || does. But that doesn’t really make sense, so you have to be explicit and write the whole thing.
Anyway, that’s “or.” The other logical operators are && (“and”) and ! (“not”). Let’s see how they work:
i_am_chris = true |
i_am_purple = false |
i_like_beer = true |
i_eat_rocks = false |
|
puts i_am_chris && i_like_beer |
puts i_like_beer && i_eat_rocks |
puts i_am_purple && i_like_beer |
puts i_am_purple && i_eat_rocks |
puts |
puts i_am_chris || i_like_beer |
puts i_like_beer || i_eat_rocks |
puts i_am_purple || i_like_beer |
puts i_am_purple || i_eat_rocks |
puts |
puts !i_am_purple |
puts !i_am_chris |
true |
false |
false |
false |
|
true |
true |
true |
false |
|
true |
false |
The only one of these that might trick you is ||. In English, we often use “or” to mean “one or the other, but not both.” For example, your mom might say, ”For dessert, you can have pie or cake.” She did not mean you could have them both! A computer, on the other hand, uses || to mean “one or the other, or both.” (Another way of saying it is “at least one of these is true.”) This is why computers are more fun than moms. (Obviously I think my mom is far less likely to read this book than my wife is.)
Just to make sure everything is well cemented for you, let’s look at one more example before you go it alone. This will be a simulation of talking to my son, C, back when he was 2. (Just for background, when he talks about Ruby, Nono, and Emma, he is referring to his baby sister, Ruby, and his friends Giuliano and Emma. He manages to bring everyone he loves into every conversation he has. And yes, we did name our children after programming languages. And yes, my wife is the coolest woman ever.) So, without further ado, this is pretty much what happens whenever you ask C to do something:
while true |
puts 'What would you like to ask C to do?' |
request = gets.chomp |
|
puts 'You say, "C, please ' + request + '"' |
|
puts 'C's response:' |
puts '"C ' + request + '."' |
puts '"Papa ' + request + ', too."' |
puts '"Mama ' + request + ', too."' |
puts '"Ruby ' + request + ', too."' |
puts '"Nono ' + request + ', too."' |
puts '"Emma ' + request + ', too."' |
puts |
|
if request == 'stop' |
break |
end |
end |
Let’s chat with C a bit.
<= | What would you like to ask C to do? |
=> | eat |
<= | You say, "C, please eat" |
C’s response: | |
"C eat." | |
"Papa eat, too." | |
"Mama eat, too." | |
"Ruby eat, too." | |
"Nono eat, too." | |
"Emma eat, too." | |
| |
What would you like to ask C to do? | |
=> | go potty |
<= | You say, "C, please go potty" |
C’s response: | |
"C go potty." | |
"Papa go potty, too." | |
"Mama go potty, too." | |
"Ruby go potty, too." | |
"Nono go potty, too." | |
"Emma go potty, too." | |
| |
What would you like to ask C to do? | |
=> | hush |
<= | You say, "C, please hush" |
C’s response: | |
"C hush." | |
"Papa hush, too." | |
"Mama hush, too." | |
"Ruby hush, too." | |
"Nono hush, too." | |
"Emma hush, too." | |
| |
What would you like to ask C to do? | |
=> | stop |
<= | You say, "C, please stop" |
C’s response: | |
"C stop." | |
"Papa stop, too." | |
"Mama stop, too." | |
"Ruby stop, too." | |
"Nono stop, too." | |
"Emma stop, too." | |
|
Yeah, that’s about what it was like. You couldn’t sneeze without hearing about Emma or Nono sneezing, too. ☺
3.133.133.61