Ruby Programming/Data types

From Wikibooks, the open-content textbooks collection

Jump to: navigation, search

← Ruby basics | Writing methods →


Contents

[edit] Ruby Data Types

As mentioned in the previous chapter, everything in Ruby is an object. Everything has a class. Don't believe me? Try running this bit of code:

h = {"hash?" => "yep, it's a hash!", "the answer to everything" => 42, :linux => "fun for coders."}
puts "Stringy string McString!".class
puts 1.class
puts nil.class
puts h.class
puts :symbol.class

See? Everything is an object. Every object has a method called class that returns that object's class. You can call methods on pretty much anything. Earlier you saw an example of this in the form of 3.times. (Technically when you call a method you're sending a message to the object, but I'll leave the significance of that for later.)
Something that makes this extreme object oriented-ness very fun for me is the fact that all classes are open, meaning you can add variables and methods to a class at any time during the execution of your code. This, however, is a discussion of datatypes.

[edit] Constants

We'll start off with constants because they're simple. Two things to remember about constants:
1. Constants start with capital letters. Constant is a constant. constant is not.
2. You can change the values of constants, but Ruby will give you a warning. (Silly, I know... but what can you do?)
Congrats. Now you're an expert on Ruby constants.

[edit] Symbols

So did you notice something weird about that first code listing? "What the hell was that colon thingy about?" Well, it just so happens that Ruby's object oriented ways have a cost: lots of objects make for slow code. Every time you type a string, Ruby makes a new object. Regardless of whether two strings are identical, Ruby treats every instance as a new object. You could have "live long and prosper" in your code once and then again later on and Ruby wouldn't even realize that they're pretty much the same thing. Here is a sample irb session which demonstrates this fact :

irb> "live long and prosper".object_id
=> -606662268
irb> "live long and prosper".object_id
=> -606666538

Notice that the object ID returned by irb Ruby is different even for the same two strings.

To get around this memory hoggishness, Ruby has provided "symbols." Symbols are lightweight objects best used for comparisons and internal logic. If the user doesn't ever see it, why not use a symbol rather than a string? Your code will thank you for it. Let us try running the above code using symbols instead of strings :

irb> :my_symbol.object_id
=> 150808
irb> :my_symbol.object_id
=> 150808

Symbols are denoted by the colon sitting out in front of them, like so: :symbol_name

[edit] Hashes

Hashes are like dictionaries, in a sense. You have a key, a reference, and you look it up to find the associated object, the definition.

The best way to illustrate this, I think, is simply to demonstrate:

hash = {:leia => "Princess from Alderaan", :han => "Rebel without a cause", :luke => "Farmboy turned Jedi"}
puts hash[:leia]
puts hash[:han]
puts hash[:luke]

This code will print out "Princess from Alderaan", "Rebel without a cause", and "Farmboy turned Jedi" in consecutive order. I could have also written this like so:

hash = {:leia => "Princess from Alderaan", :han => "Rebel without a cause", :luke => "Farmboy turned Jedi"}
hash.each do |key, value|
     puts value
end

This code cycles through each element in the hash, putting the key in the key variable and the value in the value

variable, which is then printed out.

If I wanted to be verbose about defining my hash, I could have even written it like this:

hash = Hash.new(:leia => "Princess from Alderaan", :han => "Rebel without a cause", :luke => "Farmboy turned Jedi")
hash.each do |key, value|
     puts value
end

If I felt like offing Luke, I could do something like this:

hash.delete(:luke)

Now Luke's no longer in the hash. Or lets say I just had a vendetta against farmboys in general. I could do this:

hash.delete_if {|key, value| value.downcase.match("farmboy")}

This looks at each key-value pair and deletes it if the block of code following it returns true. In the block of code you see

there, I made the value lowercase (in case the farmboys decided to start doing stuff like "FaRmBoY!1!") and then checked to see if

"farmboy" matched anything in its contents. I could have used a regular expression, but that's another story.

If I felt like adding Lando into the mix, I'd just assign a new value to the hash like so: hash[:lando] = "Dashing and debonair city

administrator." If I felt like measuring this hash, I'd just do hash.length. If I felt like looking at keys, I'd just use

the inspect method to return the hash's keys as an array. Speaking of which...

[edit] Arrays

Arrays are a lot like hashes, but the keys are always consecutive numbers. Also, the first key in an array is always 0. So in an array with 5 items, the last element would be found at array[4] and the first element would be found at array[0]. In addition, all the methods you just learned with hashes can also be applied to arrays. Here are two ways to create arrays:

array1 = ["hello", "this", "is", "an", "array!"]
array2 = []
array2 << "This"
array2 << "is"
array2 << "also"
array2 << "an"
array2 << "array!"

As you may have guessed, the << operator pushes values onto the end of an array. So if I were to write puts array2[4] after declaring those two arrays, the computer would print out array! Of course, if I felt like simultaneously getting array! and deleting it from the array, I could just pop it off. The pop method returns the last element in an array and then deletes it from the array. So if I ran this:

string = array2.pop

Then string would hold array! and array2 would be one element shorter.
If I kept doing this, array2 wouldn't hold any elements. I can check for this condition by calling the empty? method. For example, the following bit of code moves all the elements from one array to another:

array1 << array2.pop until array2.empty?

Here's something that really excites me: arrays can be subtracted and added to each other. I don't know about any other languages, but I know that Java would make a fuss if I tried the following bit of code:

array3 = array - array2
array4 = array + array2

Now array3 holds all the elements that array did except for the ones that also happened to be in array2. So all the elements of array minus the elements of array2 are now held in array3. array4 now holds all the elements of both array and array2.
You wanna' see if array has something in it? Just ask it with the include? method, like so: array.include?("Is this in here?")
If you just wanted to turn the whole array into a string, you'd do something like this:

string = array2.join(" ")

Using the array we just declared, string now holds This is also an array! If we had wanted something more along the lines of Thisisalsoanarray! we could have called the join method without any arguments, like so:

string = array2.join

[edit] Strings

I would recommend reading the chapters on strings and alternate quotes now if you haven't already. This chapter is going to cover some pretty spiffy things with strings and just assume that you already know the information in these two chapters.
In Ruby, there are some pretty cool built-in functions where strings are concerned. For example, you can multiply them. "Danger, Will Robinson!" * 5 yields Danger, Will Robinson!Danger, Will Robinson!Danger, Will Robinson!Danger, Will Robinson!Danger, Will Robinson! You can also compare strings like so: "a" < "b"
This comparison will yield true. When you compare two strings like this, you are really comparing the ASCII values of the characters. To find out what the ASCII value of a character is, use the following code:

puts ?A

This will return 65, as this is the ASCII value of "A." Just replace A with whatever character you want to inquire about. To do the opposite conversion, use the chr method. So 65.chr would return A.
Aside from these weird little operations, we also have the venerable "concatenate" operation. It's the same as in most other languages: +
So "Hi, this is " + "a concatenated string!" would return Hi, this is a concatenated string!
You can also do some interpolation, for handling pesky string variables without using the concatenate operator. string1 and string2 in the following chunk of code are identical.

thing1 = "Red fish, "
thing2 = "blue fish."
string1 = thing1 + thing2 + " And so on and so forth."
string2 = "#{thing1 + thing2} And so on and so forth."

If you wanted to step through each one of the letters in thing1, you would use the scan method like so:

thing1.scan(/./) {|letter| puts letter}

This would print out each letter in thing1. But what's with that weird "/./" thing in the parameter? That, my friend, is called a "regular expression." They're helpful little buggers, quite powerful, but they're also outside the scope of this discussion. All you need to know for now is that /./ is "reg-ex" speak for "every individual character." If I had typed /../ then it would have gone through every chunk of two characters.
Another use for regular expressions can be found with the =~ operator. You can check to see if a string matches a regular expression using this. For example:

puts "Yeah, there's a number in this one." if "C3-P0, human-cyborg relations" =~ /[0-9]/

This will print out Yeah, there's a number in this one. The match method works much the same way, except it can accept a string as a parameter as well. This is helpful if you're getting regular expressions from a source outside the code. Here's what it looks like in action:

puts "Yep, they mentioned Jabba in this one." if "Jabba the Hutt".match("Jabba")

Alright, that's enough about the regular expressions. Even though you can use regular expressions with these next two methods, we'll just use regular old strings. Lets pretend you work at the Ministry of Truth and you need to replace a word in a string with another word. You'd do it like this:

string1 = "2 + 2 = 4"
string2 = string1.sub("4", "5")

Now string2 contains 2 + 2 = 5. But what if the string contains lots of lies like the one you just corrected? sub only replaces the first occurrence of a word! I guess you could cycle through the string using match and a while loop, but there's a much more efficient way to do things. Observe:

winston = %q{   Down with Big Brother!
		Down with Big Brother!
		Down with Big Brother!
		Down with Big Brother!
		Down with Big Brother!}
winston.gsub("Down with", "Long live")

Big Brother would be proud. gsub is the "global substitute" function. Every occurrence of "Down with" has now been replaced with "Long live" so now winston is only proclaiming its love for Big Brother, not its disdain thereof.
On that happy note, lets move on to integers and floats. (If you want to know more methods you can use with strings, look at the end of this chapter for a quick reference table.)

[edit] Numbers (Integers and Floats)

You can skip this paragraph if you know all the standard number operators. For those who don't, here's a crash course. + adds two numbers together. - subtracts them. / divides. * multiplies. % returns the remainder of two divided numbers.

Alright, integers are numbers with no decimal place. Floats are numbers with decimal places. 10 / 3 yields 3 because dividing two integers yields an integer. Since integers have no decimal places all you get is 3. If you tried 10.0 / 3 you would get 3.33333... If you have even one float in the mix you get a float back. Capice?

Alright, let's get down to the fun part. Everything in Ruby is an object, let me reiterate. That means that pretty much everything has at least one method. Integers and floats are no exception. First I'll show you some integer methods.
Here we have the venerable times method. Use it whenever you want to do something more than once. Examples:

puts "I will now count to 100..."
100.times {|number| puts number}
5.times {puts "Guess what?"}
puts "I'm done!"

This will print out the numbers 1 through 100, print out Guess what? five times, then say I'm done! It's basically a simplified for loop. It's a little slower than a for loop by a few hundredths of a second or so; keep that in mind if you're ever writing Ruby code for NASA. ;-)

Alright, we're nearly done, six more methods to go. Here are three of them:

# First a visit from The Count...
1.upto(10) {|number| puts "#{number} Ruby loops, ah-ah-ah!"}

# Then a quick stop at NASA...
puts "T-minus..."
10.downto(1) {|x| puts x}
puts "Blast-off!"

# Finally we'll settle down with an obscure Schoolhouse Rock video...
5.step(50, 5) {|x| puts x}

Alright, that should make sense. In case it didn't, upto counts up from the number it's called from to the number passed in its parameter. downto does the same, except it counts down instead of up. Finally, step counts by the first number in its parameter from the number its called from to the second number in its parameter. So 5.step(25, 5) {|x| puts x} would output every multiple of five starting with five and ending at twenty-five.

Time for the last three:

string1 = 451.to_s
string2 = 98.6.to_s
int = 4.5.to_i
float = 5.to_f

to_s converts floats and integers to strings. to_i converts floats to integers. to_f converts integers to floats. There you have it. All the data types of Ruby in a nutshell. Now here's that quick reference table for string methods I promised you.

[edit] Additional String Methods

# Outputs 1585761545
"Mary J".hash

# Outputs "concatenate"
"concat" + "enate"

# Outputs "Washington"
"washington".capitalize

# Outputs "uppercase"
"UPPERCASE".downcase

# Outputs "LOWERCASE"
"lowercase".upcase

# Outputs "Henry VII"
"Henry VIII".chop

# Outputs "rorriM"
"Mirror".reverse

# Outputs 810
"All Fears".sum

# Outputs cRaZyWaTeRs
"CrAzYwAtErS".swapcase

# Outputs "Nexu" (next advances the word up one value, as if it were a number.)
"Next".next

# After this, nxt == "Neyn" (to help you understand the trippiness of next)
nxt = "Next"
20.times {nxt = nxt.next}
Personal tools
Create a book