Decisions, decisions (Ruby tutorial)

From Compsci.ca Wiki

Jump to: navigation, search

In programming the ability to control the flow of the program is critical. Ruby provides ample facilities for doing this.

In this tutorial I'll modify the previous tutorial's code to allow the computer to respond to my input differently depending on what I input.

It should respond as it did before if I input a normal name. However, if I input nothing, it should respond rudely, and if I input the names Calvin, Linus, or Sid, it should emit a Nelson-esque "Haaaaa Haaaa!".

First, though, I'd like to make one small addition to the code. As I posted it before, it simply reads a line of input, assigns it to the variable name, then inserts it into another string, which it prints. Unfortunately, this doesn't test for whether or not I input a bunch of spaces at the beginning or end of my name. I want to get rid of extraneous whitespace.

puts "Hello, Ruby!"
print "I'm "
name = gets
name.strip!
puts "Ruby says, \"Hello, #{name}\""

The extra line calls the strip! method on the object we've given the name "name". "strip" indicates that it's stripping the extraneous whitespace off, while the ! is a visual indication that the method is changing the object permanently.

name.strip

The above would simply create a new string object, remove the whitespaces in that new object, and return it. In fact, we can use this method to further streamline the new code, by removing the extra whitespace before the object is ever assigned to name.

puts "Hello, Ruby!"
print "I'm "
name = gets.strip
puts "Ruby says, \"Hello, #{name}\""

Note: As in Java, and as in many other languages, the dot is used to access methods in Ruby.

Contents

If.... I only had a brain...

Ruby uses the if statement in much the same way as other languages.

puts "Hello, Ruby!"
print "I'm "
name = gets.strip

if name == ""
   puts "Ruby says, \"fine, ignore me!\""
elsif name == "Calvin" or name == "Linus" or name == "Sid"
   puts "Ruby says, \"Haaaaa haaaaa!\""
else
   puts "Ruby says, \"Hello #{name}\""
end

Now, we could probably streamline this a bit and remove the redundant code by just sticking Ruby's message to us in a variable and then doing the printing later on.

puts "Hello, Ruby!"
print "I'm "
name = gets.strip

if name == ""
   statement = "fine, ignore me!"
elsif name == "Calvin" or name == "Linus" or name == "Sid"
   statement = "Haaaaa haaaaa!"
else
   statement = "Hello #{name}"
end

puts "Ruby says, \"#{statement}\""

We're still not using the language to it's full potential, though, and all of those redundant "statement = " lines look bad. They look bad to me because I know that Ruby's if...elsif...end construct can return values as in the following code.

puts "Hello, Ruby!"
print "I'm "
name = gets.strip

statement = if name == ""
   "fine, ignore me!"
elsif name == "Calvin" or name == "Linus" or name == "Sid"
   "Haaaaa haaaaa!"
else
   "Hello #{name}"
end

puts "Ruby says, \"#{statement}\""

We didn't even need the "return", even though Ruby has it, and I could have used it.

Though it's not truly a control structure, it's used so often in them that I feel obligated to offer a brief explanation of regular expressions here, since it can greatly simplify our test to see if Ruby should laugh at you.

A regular expression is a pattern against which text can be matched. A simple regular expression for our purposes would look like:

/Calvin|Linus|Sid/

The slashes Ruby treats as a form of quotes which just happens to quote a regular expression. The | indicates the pattern should match either Calvin, Linus, or Sid. In this case it will match if they occur anywhere in the input name. Of course, one may argue that someone is safer if only their middle name is Linus, but then again, they deserve the ridicule if they mention that publicly.

Note: No offense is intended to anyone actually named Calvin, Linus, or Sid.

To match a reular expression against a string, we use the =~ operator, so our code now becomes the following.

puts "Hello, Ruby!"
print "I'm "
name = gets.strip

statement = if name == ""
   "fine, ignore me!"
elsif name =~ /Calvin|Linus|Sid/
   "Haaaaa haaaaa!"
else
   "Hello #{name}"
end

puts "Ruby says, \"#{statement}\""

Now I should mention that including the "then" keyword allows for a great deal of flexibility in how the code is layed out.

puts "Hello, Ruby!"
print "I'm "
name = gets.strip

statement = if name == "" then "fine, ignore me!" elsif name =~ /Calvin|Linus|Sid/ then "Haaaaa haaaaa!" else "Hello #{name}" end

puts "Ruby says, \"#{statement}\""

Just in Case

Of course, that last example might look better if we knew about Ruby's case construct. Similar to other languages' "switch", Ruby's counterpart is more powerful since it can work on types other than just integers.

puts "Hello, Ruby!"
print "I'm "
name = gets.strip

statement = case name
   when ""
      "fine, ignore me!" 
   when /Calvin|Linus|Sid/
      "Haaaaa haaaaa!" 
   else 
      "Hello #{name}" 
end

puts "Ruby says, \"#{statement}\""

But that includes a lot of extra whitespace to make it clear how this construct works. Again, "then" can compact it all down.

puts "Hello, Ruby!"
print "I'm "
name = gets.strip

statement = case name when "" then "fine, ignore me!" when /Calvin|Linus|Sid/ then "Haaaaa haaaaa!" else "Hello #{name}" end

puts "Ruby says, \"#{statement}\""

Even now, with all of this, I've neglected Ruby's postfix conditionals. Yep, we can actually stick the "if" at the end for quick one-liners.

In the following we'll make sure Ruby doesn't look inarticulate by having it not say anything in the odd event that the statement variable is empty.

puts "Hello, Ruby!"
print "I'm "
name = gets.strip

statement = case name when "" then "fine, ignore me!" when /Calvin|Linus|Sid/ then "Haaaaa haaaaa!" else "Hello #{name}" end

puts "Ruby says, \"#{statement}\"" if statement != ""

More clear in this case though is the keyword "unless", which we can use anywhere "if" is used (though there's no elsunless, yet).

puts "Hello, Ruby!"
print "I'm "
name = gets.strip

statement = case name when "" then "fine, ignore me!" when /Calvin|Linus|Sid/ then "Haaaaa haaaaa!" else "Hello #{name}" end

puts "Ruby says, \"#{statement}\"" unless statement == ""

But == "" looks so busy. Wouldn't it be great if Ruby's strings had a method which could determine if the string is empty for us, in a way that lends more meaning than what we're currently using?

Oh, they do.

puts "Hello, Ruby!"
print "I'm "
name = gets.strip

statement = case name when "" then "fine, ignore me!" when /Calvin|Linus|Sid/ then "Haaaaa haaaaa!" else "Hello #{name}" end

puts "Ruby says, \"#{statement}\"" unless statement.empty?

The question mark at the end of the empty? method is a good visual indicator that the method return a boolean (true or false) value.

So, how can we make this even shorter, just because we're 1337 hackers?

Well, I mentioned in the "Hello, Ruby!" tutorial that the #{} construct was more powerful than the example I gave indicated, and it is. In addition to just inserting variables into a string, it can insert entire expressions.


puts "Hello, Ruby!"
print "I'm "
name = gets.strip

puts "Ruby says, \"#{case name when "" then "fine, ignore me!" when /Calvin|Linus|Sid/ then "Haaaaa haaaaa!" else "Hello #{name}" end}\"" unless statement.empty?

That's all well and good, but now there's no statement variable to test at the end.

puts "Hello, Ruby!"
print "I'm "
name = gets.strip

puts "Ruby says, \"#{statement = case name when "" then "fine, ignore me!" when /Calvin|Linus|Sid/ then "Haaaaa haaaaa!" else "Hello #{name}" end}\"" unless statement.empty?

And now there is!

One last thing

Warning: Crazy compacting of code follows.  ;-)

Now, let's say I don't need to use the variable name anywhere else after this. Why should I create it at all?

puts "Hello, Ruby!"
print "I'm "

puts "Ruby says, \"#{statement = case gets.strip when "" then "fine, ignore me!" when /Calvin|Linus|Sid/ then "Haaaaa haaaaa!" else "Hello #{name}" end}\"" unless statement.empty?

What lets us do this now all in one line of code is that not only can you put a single expression into a string, but you can put many, separated by semi-colons. The return value of the last expression is what will be inserted into the string.

It should be noted that quotes can work oddly inside #{}, so we have to oddly quote "I'm " in the following.

puts "Ruby says, \"#{puts "Hello, Ruby!"; print 'I\'m '; statement = case gets.strip when "" then "fine, ignore me!" when /Calvin|Linus|Sid/ then "Haaaaa haaaaa!" else "Hello #{name}" end}\"" unless statement.empty?

That's probably enough mind-warping code for one tutorial.

Credit

Author: Wtd

Personal tools