The Basics of Variable Assignment in Ruby
Variables are one of the cornerstones of programming, and, like anything, understanding them is key to being able to use them effectively.
Programmers assign pieces of data to variables for many reasons, but I will list a few of the main ones so that you have a very general understanding:
- Variables are often easier to remember and type than the actual information we are working with (e.g. — vs ‘1234 Amityville Lane, Apt 3., Anytown, New York, 67890’).
- Programmers often want to start with a piece of data in one state but end with it in a maybe-quite-complicated different state, and assigning data to variables makes that process much easier to manage and follow.
- Assigning data to variables gives code longterm manageability. Programmers come and programmers go, but programs exist until they are deleted. With the right variable names, any programmer can look at your code — even after you’ve left for another opportunity — and know what is going on, which is key to managing code longterm.
Basic Variable Assignment
A quick exploration of Ruby variables and their behaviors reveals a lot, and hopefully you are here to do just that, explore. Feel free to open up interactive ruby (irb) in your terminal and code along to test this stuff out. Let’s start with the most basic of basics, the assigning of variables to barewords:a = "Ruby"
In the above code, we have assigned a value of to the bareword, which tells our program that from now on when we use we are really talking about the string . This is clearly shown by just typing the letter again and hitting :a
But, this still leaves some questions as to the actual relationship between our variable and it’s value. Let’s gather a bit more information by asking our variable for it’s :a
Every bit of data that is created during the running of software is stored in memory, and all of those bits of data are associated with storage reference points so that the computer can find them again if need be. Since (almost) everything is an object in Ruby*, we can run the standard method on the variable to see its memory address. In this case, the return value for is (If you’re coding along, your memory address will be different than this one. Don’t be alarmed.) Let’s try assigning the string to another variable and see what happens to learn more:
*(If you don’t know what I mean here, you can pause now to read this or any other tutorial of Ruby objects on the web. You don’t necessarily need to fully understand Ruby’s object model to get a general understanding of variables to start, but knowledge of it will certainly enhance your ability to really internalize Ruby’s take on variables.)a = "Ruby"a
=> 70211214754080b = "Ruby"b.object_id
Hm. As you can see, the memory addresses of and are different even though they both appear to equal the same thing, and if you are like I was when I first started out, you are probably wondering why. This is because of the way(s) in which Ruby utilizes variable assignments AND the way(s) in which Ruby deals with strings (and other objects, for that matter). When we initially assign the string to the bareword , the computer creates a new string object of since that string can’t exist in memory until we type it in. The letters, numbers, and/or symbols of the string are (mostly) irrelevant to the computer — and to us for the moment. The only thing that matters at this point is that our actions create a string and that said string is now stored in the location in memory. By assigning that new string to the variable , we are simply telling the computer that we want to use as a reference point for the string object that we just created — so that we don’t have to remember . From this point on in our program, if we type , Ruby will know that what we really mean is the piece of data that is located at in memory which also happens to have the value of “Ruby” as per our assignment, though, the computer doesn’t actually care about that.
This helps explain why ’s location in memory is different than ’s. is also a reference point to a memory location of a new string that we have created, regardless of it’s value. The fact that ’s string object also says is of no significance to the computer while the object is stored away. It just takes what we create and stores it for potential later use and agrees to let us call it until further notice. ’s string object and ’s string object contain the same values, but they are different string objects that live at different temporary addresses in our computer.
Slightly More Advanced —Variables Are Pass-By-Value In Ruby
First, let’s look at a bit more code:a = "Ruby"b = aa
We assigned to , assigned to to , and can now see that and are both pointing to the same object in memory by calling . Simple. But, there is still more to know…
Ruby is a pass-by-value language (as opposed to a pass-by-reference language). When we assign to , we are simply telling to reference whatever it is that is referencing, in this case, our string object . The computer connects directly to that string object. It does not connect to the object through . This means that if we reassign and give the computer a new object for it to reference, will still refer to the old string object. See below:a = "Ruby"b = aa
=> 70211214754080a = "I have changed"b
This same scenario, however, would play out differently if we simply modified our string object instead of creating a new one and telling to reference that one instead. Look:a = "Ruby"b = aa
=> "Ruby"a << " is great"
=> "Ruby is great"b
=> "Ruby is great"
Since we simply mutated our original string object but did not create a new one and assign to it, and are both still referencing the same original string object and, thus, still hold the same value.
Another — and perhaps easier — example can be seen with an array. I’ll comment explanations of what is going on:#assign a to an array value with 3 elements
a = [1,2,3]#get a's object_id for reference
=> 70211214067539#assign b to whatever memory location a is currently referencing
b = a#confirm that b has the same object_id as a
=> 70211214067539#add a new element to the a array
a << 4
=> [1,2,3,4]#see that a is still pointing to the same memory location
=> 70211214067539#confirm that b now has that new element, too
=> [1,2,3,4]#assign a to a new array
a = [5,6,7,8]#see that a's memory location reference has changed (because we created a new piece of data for it to reference)
=> 70211214001628#see that b is still referencing the original object
Hopefully, at this point you have a general understanding of variable assignment in Ruby. There are definitely more advanced topics to be discussed concerning variables, but for the early stages, there’s just a few key things to remember:
- Variables in Ruby are references to locations in memory, not references to values themselves
- Variables in Ruby are pass-by-value, not pass-by-reference (Some people say that Ruby is a pass-by-reference-value language, but that’s a conversation for another day.)
- Mutating an existing piece of data will return the newer version of that data to any variable(s) pointing to it’s memory location
- Creating a new piece of data and assigning an existing variable to it will change the memory location reference (and thus the value) of that variable but will not affect any other variable(s) that happen to point to the memory location of the original piece of data
If you walk away from this post knowing these few things, you’ll have a lot more to learn, but you’ll definitely be on the right track.
“The most important property of a program is whether it accomplishes the intention of its user.” — C.A.R. Hoare
|Purchase and download the full PDF and ePub editions of this Ruby eBook for only $8.99|
In this chapter of Ruby Essentials we will look at implementing flow control logic in Ruby scripts through the use of the Ruby case statement.
Ruby Flow Control
In the previous chapter we looked at some basic conditional structures using the if ... else and if .. elsif ... mechanisms. These approaches to building conditional logic work well if you need to check a value against only a few different criteria (for example checking the value of a string against a couple of possible candidates):if customerName == "Fred" print "Hello Fred!" elsif customerName == "John" print "Hello John!" elsif customername == "Robert" print "Hello Bob!" end This can quickly become cumbersome, however, when a need arises to evaluate a large number of conditions. A much easier way to handle such situations is to use the Ruby case statement, the syntax for which is defined as follows:
result = case value
when match1 then result1
when match2 then result2
when match3 then result3
when match4 then result4
when match5 then result5
when match6 then result6
There can be any number of when statements - basically as many as you need to fully compare the value in the case statement against the possible options (represented by match1 through to match7 in the above example) specified by the when statements. When a match is found the result is assigned to the optional result variable.
Finally, the else statement specifies the default result to be returned if no match is found.
This concept is, perhaps, best explained using an example. The following Ruby case statement is designed to match a particular car model with a manufacturer. Once a match is found, the car and associated manufacturer are included in an output string:car = "Patriot" manufacturer = case car when "Focus" then "Ford" when "Navigator" then "Lincoln" when "Camry" then "Toyota" when "Civic" then "Honda" when "Patriot" then "Jeep" when "Jetta" then "VW" when "Ceyene" then "Porsche" when "Outback" then "Subaru" when "520i" then "BMW" when "Tundra" then "Nissan" else "Unknown" end puts "The " + car + " is made by " + manufacturer
When executed, the resulting output will read:The Patriot is made by Jeep
If no match was found in the case statement, then the default result, defined by the else statement would cause the following output to be generated:The Prius is made by Unknown
Number Ranges and the case statement
The case statement is also particularly useful when used in conjunction with number ranges (for details of ranges read the Ruby Ranges chapter of this book).
The following case example detects where a number falls amongst a group of different ranges:score = 70 result = case score when 0..40 then "Fail" when 41..60 then "Pass" when 61..70 then "Pass with Merit" when 71..100 then "Pass with Distinction" else "Invalid Score" end puts result
The above code, when executed will result in a "Pass with Merit" message.
The if .. else ... approach to building conditional logic into an application works fine for evaluating a limited number of possible criteria. For much larger evaluations, the Ruby case statement is a less cumbersome alternative. In the chapter we have looked at the case statement and reviewed some examples using strings and number ranges.
|Purchase and download the full PDF and ePub editions of this Ruby eBook for only $8.99|