A Programming Primer for Counting and Other Unconventional Tasks

Variables

Labels for pointing to your data.

Our programming so far has been very literal.

A 1 is just a 1.

The string "dog" is just the word "dog".

These objects in Ruby are what they are. But take the quotation marks out of "dog". Then, insert an equals sign =

And finally, on that same line, provide a data object on the the right side of that equals sign.

dog = "cat"

We have just declared a variable with the name of dog. This quote-less (also referred to as a bareword) term, dog, is not a string. But it has been assigned to a string. And that string is "cat":

dog = "cat"
puts dog
#=> cat

The variable dog refers to the value "cat". This means that anything that interacts with the variable dog is essentially interacting with the value "cat":

dog = "cat"
puts(dog + "food")   
#=> catfood

That example may seem straightforward. And for the most part, variables are that straightforward to understand. However, this chapter may be one of the more thorough and convoluted chapters in this book.

Not because the concept of variables is difficult. But because it is different enough from most non-programming kinds of thinking.

Variables are easy once you get them. And their existence enables human programmers to easily write powerful code. But without a clear (though not necessarily exhaustive) understanding of variables, programming will be incredibly mystifying.

Be forewarned, this section is long on abstract concepts and short on practical application. This is because variables only become really interesting and usable after you learn about methods, conditional statements, and loops – which, incidentally, are the chapters that follow right after.

But all of these chapters depend on variables. And without a good understanding of variables, you are entering a world of frustration. You will see programming as arcane and befuddling, and then as electronic masochism, after which it won't be a week before you've forsaken programming and perhaps computers entirely.

There's a lot of material here because I wasn't quite sure how to best cover it. This chapter basically tries to explain the same concept about variables three different ways:

  1. Real-life analogies
  2. Examples through code
  3. Pictures

Hopefully, at least one of these teaching methods is effective.

One more thing: Don't worry about memorization. That is, it's ok to mix up the exact meanings of:

  1. a = a + 42
  2. a += 42
  3. a << 42

It is only important to grok the fundamentals in such a way that you can conceive why there are different meanings, even if you forget which expression means what.

Once you get the big picture, you can move on to the other lessons and start to use variables in very useful and powerful ways. And then, you'll easily memorize the details.

Useful labels for your data

In the String lesson, we learned that strings must be set off by quotation marks. Variables, which are words not enclosed in quotes, serve as human-readable labels for data objects in the computer's memory.

In Ruby, variables are sometimes referred to as pointers and references; they point to objects. They are references to objects.

The most important impact from variables for our programming is that we can use them to store values for later. For example:

a = "supercalifragilistic"
b = "expialidocious"
puts "The length of the words #{a} and #{b} is:"
puts a.length + b.length
puts "#{a} is #{a.length - b.length} characters longer than #{b}"
The length of the words supercalifragilistic and expialidocious is:
34
supercalifragilistic is 6 characters longer than expialidocious

This is the convoluted statement you'd have to write without using variables:

puts "The length of the words #{'supercalifragilistic'} and #{'expialidocious'} is:"
puts "supercalifragilistic".length + "expialidocious".length
puts "supercalifragilistic is #{'supercalifragilistic'.length - 'expialidocious'.length} characters longer than expialidocious"

In retrospect, I probably should've started with variables earlier. The past two chapters of typing out numbers and strings probably made programming seem unnecessarily tedious.

How a dog is a "cat"
dog = "cat"

By typing the word dog and then setting it equal to the value "cat", we have told the Ruby interpreter that any use of dog is a reference to the data stored in that memory location (which Ruby allocates for you). And right now, that piece of memory holds the "cat" string

In data-gathering programs, you will use variables to refer to quantities that you won't know the exact value until the program actually runs. In fact, if you had known what those quantities were, you probably wouldn't write a program in the first place.

Here is pseudo-code (i.e. it won't actually execute) to find out how many characters are currently on the Wikipedia homepage:

puts "Wikipedia".length  
#=> 9  [...We could've counted that ourselves]

my_variable_for_wikipedia_homepage = some_method_to_get_webpages("http://wikipedia.org")   
#   We use a variable to hold the result of a command
#   that has downloaded the page from http://wikipedia.org

puts "Wikipedia's homepage is #{my_variable_for_wikipedia_homepage.length} characters long"
#=>   Wikipedia's homepage is 139233 characters long
         

Storing the result of that webpage-downloading-method into a variable lets you, the programmer, access its value (the text of the webpage) without knowing beforehand what the value would be.

As you'll see, variables are one of the basic concepts that makes programming immensely useful in solving informational problems.

Explaining variables with analogies

This next section contains no code. Just fumbling attempts at narrative and metaphor to explain the concepts of variables if the previous section left you confused.

If you know nothing about programming today, you might re-read this section tomorrow and wonder why this section is so long and wordy. But I'm trying a non-code method for explaining variables in the hopes that real-world analogies, limited as they are, might serve as a starting point.

As "easy" as 1-2-3

I don't mean to intimidate you. Variables are so easy and basic a concept that once you get them, you can't possibly imagine how you ever didn't get them.

In that way, variables are much like numbers. Consider the following questions involving numbers:

  • What is 1 + 1?
  • Is 3 greater than 8?
  • Is 100 at least 10 times as much as 9?

The answers to these questions came so easily that you don't even have to engage the higher-thinking-part of your brain. Declaring that 8 is more than 3 comes as instinctively as knowing – by sight alone – that a carton of 8 eggs has more eggs than a carton of 3 eggs.

But imagine trying to teach that basic concept of numbers to an otherwise intelligent adult who can speak but in a bizarre accident of his educational system, never learned or had even seen a written language. It's not that he has to recognize that the particular squiggly line for the number 3 is lower in the sequence of numbers than the squiggly line representing 8 (though that is obviously important to know).

You actually have to teach him a concept even more basic: How a written symbol can stand for something to which it bears no physically apparent relation.

8 scrawl
Numbers in grade school.

That concept includes these corollaries, all of which are brain-dead obvious to everyone who is able to read them:

  • 8 is greater than 3, even if 3 is written in extra-large font. It is only the signature of the symbol that matters, not any of its typeface-related properties.
  • 30 is greater than 8, even though it involves the same numerals. This is because the 0 also symbolizes a quantity.
  • 0.0008 is not necessarily greater than 3, even though it has more of that 0 character. The placement of the symbols also has meaning.

It's not a hard concept for this hypothetical illiterate adult to understand – once he gets it. Until then, there will be a brief period of time in which you'll throw your hands up in exasperation and say, "It just IS. Don't you understand?"

This is the way it is with variables and non-programmers.

So the good news is that variables are relatively easy to comprehend. They only seem difficult because they are an entirely new concept. But just because something is unfamiliar doesn't mean that it's hard.

The bad news: if you don't get them, then you cannot continue trying to learn to program. I don't mean that you should quit. I mean that if you don't understand the basic concept, then don't try to move on yet because you think you can wing it. That is like thinking "OK, I don't get this 'numbers' thing yet. But I'm sure it'll make sense after I learn this 'addition' thing."

More good news: you only need to understand the basic concept behind variables, not all of the many details. Just like the illiterate person only needs to understand that 2 can be equal to 1 and 1, not necessarily that 2 - 1 = 1 (yet).

And so you may not completely understand exactly how the following two code expressions are different:

x = "cafe"
puts( x << "mocha")
#=> "cafemocha"
x = "cafe"
puts( x + "mocha")
#=> "cafemocha"

– but you can at least know that there is a difference because of how variables refer to data objects in programs.

You should be able to explain the behavior in the following expression:

cat = "kitty"
dog = "puppy"
dog = cat
puts dog
#=> "kitty"

If not, then go and ask another programmer for an explanation. It doesn't mean that you're incapable of programming, it probably means my explanations are terrible. But just don't try to learn more about programming without getting the basics down.

A variable is not your dog's name

The one important insight that a complete beginner must understand: A variable is just a name.

The current Wikipedia definition of variable is a decent starting point for our purposes (emphasis added):

In computer programming, a variable is a symbolic name given to some known or unknown quantity or information, for the purpose of allowing the name to be used independently of the information it represents.

Ergo:

dog = "cat"
cat = "dog"

puts dog   
#=> cat

puts cat   
#=> dog

This disconnect between a variable's name and what it points to is so basic a truism to all of modern programming. And yet it seems to be one of the most common and difficult stumbling blocks for non-programmers.

I don't have any expertise in the science behind critical thinking. But I've come to believe that non-programmers fail to understand variables – not because their brains aren't wired "that way" or because variables are inherently difficult – but because in everyday normal non-programming life, labels almost always have a inextricable connection to the things they label.

Screamer
Screamer

We name our pets based on their inherent traits, such as species ("Wolfie"), gender ("Daisy"), appearance ("Spot") and behavior ("Hunter"). At the animal shelter, my dog was named "Screamer" because she drove her caretakers crazy with her endless barking. When we took ownership of her and brought her home, we couldn't just change her name because she was used to it. So we kept on calling her "Screamer" and she answered to "Screamer" for the rest of her life. After she passed, we did not name the new dog "Screamer," even though he had taken Screamer's place in our house (and was even looked a little like her).

The labels in programming – that is, the variables – have no such tie to their values. I may initialize x to the value of 100. Later in the program, x may have been incremented to 101. Then, inside an entirely different context, I may use x to temporarily refer to the contents of a downloaded webpage.

Consequently, it's important to realize that if a variable does correspond to its actual value, that's a decision made by the programmer and is only relevant to humans who have to read it. The Ruby interpreter could care less what the variable's name is. If I set the variable url_for_google_webpage to the value of "www.google.com", the Ruby interpreter won't stop me or any other programmer from later setting that variable to "www.yahoo.com"

Coat-check: variables as references

In Ruby and its contemporaries, variables are more accurately thought of as references. Consider this Ruby expression:

var_1 = "hello "
var_2 = "world"
puts var_1 + var_2

It is not technically correct to say that the variable var_1 is equal to the string "hello ". In actuality, var_1 is just a reference to where "hello " is stored in memory. And when you invoke var_1 in a program, Ruby knows to bring you the value of "hello " for your program.

Snowstorm in Times SquareSnowstorm in Times Square

If this quibbling over values and references seems pedantic and/or nonsensical, it should. Other languages distinguish between variables that actually "contain" data and those that "refer" – that is, contain the address in memory – to data.

If Ruby is your only programming language, I think it's pretty much impossible to conceive of variables that are not references. Not a big deal (until you try to learn the C language). It's just important to understand the particularly loose relationship between a Ruby variable and its data: it doesn't contain the data, it contains the address of that data.

The best real-life metaphor I can think of is the coat-check at your local fancy restaurant or cultural institution.

When its your turn at the counter, you give the attendant your coat. In return, he gives you a paper ticket with a number.

Some obvious points:

  • That number is not your coat. That is, you cannot leave the museum and wear that piece of paper to keep yourself warm.
  • That number has nothing inherently in common with your coat. Let's say the number on your ticket is 42. The person in front of you may get the number 118. So the number may not even indicate the order in which your coat was taken.

At the end of the evening, you go to the coat-check counter and say, "My coat is number 42." The attendant might double-check to see if you have the piece of paper that says "42". Either way, he understand that the number 42 is not literally your coat, but that number 42 is a reference to where he can find your coat.

There are a few other consequences to this system:

  • At any one time, your number refers to one spot in the coat rack system. It cannot (or at least, should not) point to two different coat-holding spots.
  • If your coat gets switched with the coat in spot 24, showing your ticket will get you a coat – just not the one you thought you had. It is entirely possible for the value, i.e. the content of that reference, to change even if you've kept your original ticket.
  • If your coat remains in the 42 spot but you lose the ticket that says "42", you may not get your coat back.

These same concepts and consequences apply to programming and how variables behave: The name of a variable may have nothing to do with the content it refers to, and that content can change to something unexpected if you aren't careful.

The metaphor only extends so far, of course. A real-life coat-check doesn't assign two different tickets to the same coat. And it can't make as many copies of each coat as it pleases. These constraints do not exist in programming

Back to the code

Tired of metaphors? Now we go back to covering the Ruby syntax. Later in this chapter, there is visual guide for the behavior of variables.

Basic usage of variables

If variables in your code are meant to be human-readable labels for your data, then make them readable and sensical to you. Avoid using single- or double-letter variables until you become more experienced (i.e. when you start using loops with temporary variables)

For now, here are some general guidelines for variable-naming that will keep you from trouble:

  • Start off with a lowercase letter
  • Don't use spaces or punctuation, except for underscores. e.g. my_first_variable
  • You can use numbers – but not as the first character – and uppercase letters. But to keep things simple, just stick to lowercase characters and underscores
Exercise: Name Rules

Point out the invalid variable names:

  • dog
  • "cat"
  • passw0rd
  • webpage-url
  • mcKinley_Mountain
  • 100baby_names
Solution

The invalid variable names:

  • "cat" – this is a String
  • webpage-url – can't have punctuation
  • 100baby_names – can't start with a number

Variable and method names

In our very first program, we used the word puts:

puts "Hello world"

Why isn't puts a variable? Well, in fact, the word puts does function as a label for an object. That object happens to be a method.

How can you tell if a given variable refers to data or to a method? Usually, through habit and convention. Most methods are invoked with the dot operator. And those that aren't are common enough that you'll soon recognize them, such as puts.

But the truth is, you can't automatically know that puts is a method in any given section of code. For instance:

puts = 20
puts(puts + 10)
#=> 30   

Chaos, right? But generally, you won't run into this kind of mixup unless you're taking over code from someone who was incredibly careless. As I said earlier, a variable is just a name. But what prevents anarchy is that programmers generally won't muck with already established names such as puts.

Constants

I stated above that you should stick to naming your variables with lowercase letters and underscore characters.

However, Ruby has a special convention for variables that, during the course of a program, will not change in value. These are called, as expected, constants.

PI_ESTIMATE = 3.14159265
radius = 42
the_circumference = 2 * PI_ESTIMATE * radius   #   263.8937826

Using uppercase letters for variables is mostly for human-readibility. Anyone reading your code will expect that constants aren't going to change anywhere in the program, and anyone updating your code should know not to change the value of constants after their initialization. But even if they do, Ruby will only issue a warning:

A warning for reassigning a CONSTANT
A warning for reassigning a CONSTANT

Assignments

We saw basic variable assignment in the introduction:

dog = "cat"   

If this is the first time that the bareword dog has been used in the current Ruby program, then this assignment is also where the the variable named dog is declared. This simply means that the Ruby interpreter knows of dog and you won't get this error:

NameError: undefined local variable or method `dog' for #<Object:0x1001dd2a0>
   from (irb):1

This is basically it in terms of how to create new variables and to point them to a value.

The equals sign

The symbol for connecting a variable to a value is the equals sign =

my_first_variable = 12
my_second_variable = "house"
      

In other languages, variables are explicitly declared. For example, in Javascript, the keyword var tells the interpreter that the following word is a new variable.

var my_first_variable = 12
var my_second_variable
my_second_variable = "house"
      

Ruby saves you the trouble of typing var. The upshot is that anytime you bring a new variable into existence, you have to assign it a value, even if the value is nil, which is Ruby's keyword for "nothing":

my_1st_variable = 10
my_2nd_variable = ""   #=>   an empty string
my_3rd_variable = nil   #=>   nil means nothing, which is different from an empty string
my_4th_variable   #=>   Error! Need to assign it to something, even nothing (nil)
      
Assignment is not equality

The use of the equals sign = to assign values to variables is nearly ubiquitous in programming languages. But the symbolism has its downsides.

Since grade school, we've learned that the = sign is used as part of a mathematical equation to indicate that the left side of the equation is equivalent to the right side:

2 + 3 = 5

In English, this reads as: "The sum of 2 and 3 is equal to 5"

In programming languages such as Ruby, however, this phrase reads as: "2 + 3 refers to the value 5"

That's not quite right. Because 2 + 3 is the value 5. It not as if 2 + 3 were undefined until we came along and put = 5 there.

Unfortunately, this is not just semantics-schmantics. One of the most frequent errors in programming is confusing this:

b = 56

– with this statement, which tests for equality:

b == 56

Try this for yourself:

puts x = 9
puts x == 9
puts x == 900

The output is:

9
true
false

The description of those statements:

  1. Print to screen (puts) the result of assigning x to 9
    Answer: 9
  2. Print to screen the result of testing whether what x refers to is equal to 9
    Answer: true; the variable x was just declared in the previous line.
  3. Print to screen the result of testing whether what x refers to is equal to 900
    Answer: false; as we saw in the previous two lines, x refers to 9.

We don't deal much with the equality operator (double equals signs ==), true, and false until the chapter on conditionals. But it's good to learn early that the equals sign in Ruby should not be taken literally; it will save you a lot of grief later on.

Overwriting variables

How do we make a variable refer to something else? Simply reassign it:

some_var = "red jacket"
puts some_var   #=> red jacket

some_var = "blue jacket"
puts some_var   #=> blue jacket         

If the string "red jacket" no longer has a variable pointing to it, what happens to the string "red jacket"? In the coat-check metaphor, losing your check-ticket doesn't mean that your coat is actually gone. In Ruby, the same applies to "red jacket" in that the string does occupy memory. However, it's as good as gone as the Ruby interpreter keeps track of which objects have references and tosses out the ones that don't.

Operations don't (usually) alter objects

When performing an operation involving a variable, such as:

x = 10
x + 5

That operation and most others (unless explicitly defined) will not change the object that the variable refers to:

x = "hello"
y = x + " world"

puts y
#=> hello world

puts x   
#=> hello

If we want x to contain the value of the operation affecting it, then we must explicitly assign it to that value:

x = "hello"
x = x + " dolly"

puts x   
#=> hello dolly

It's not evident now, but when Ruby performs the x + " dolly" operation, it essentially makes a copy of the object referred to by x. So until the assignment step:

x = [the result of x + " dolly"]

– the variable x still refers to the object "hello". But because both the string-adding and assignment operations happen on the same line, we aren't able to easily observe them as discrete operations.

However, if we break that line down into two discrete steps by using the variable y, we see more clearly that y uses a copy of x's object:

x = "hello"
y = x + " world"

puts y
#=> hello world

puts x   
#=> hello
Inline assignment operations

To make things a little more concise, Ruby allows you to combine arithmetic operators with the equals sign = to do an operation upon a variable and reassign it to the variable – all in a single line:

y = 10
y = 10 + 0.5   #=>   10.5

y += 0.5   #=>   11.0               
y *= 2   #=>   22.0
y /= 11   #=>   2.0
y -= 1   #=>   1.0

apple = "Washington"   #=> "Washington"
apple += " State"   #=> "Washington State"

Once again, the addition operation does not alter the original object:

x = 10.0
y = x
y += x

puts y   #=> 20
puts x   #=> 10

To emphasize the point, here's tom and jerry again:

tom = "kitty"
jerry = tom
tom += "kat"

puts jerry   #=> kitty
puts tom   #=> kittykat
Exercise: A few assignments

Let's review what we've learned about variable assignment so far. Figure out what's in the variables in the following series of statements:

x = 9
y = x + 7
y = x + 4
dog = "siamese"
cat = "chow chow"
cat += dog
a = 99
a = a + 12
b = a + c   
Solution
  1. puts x   #=> 9
    puts y   #=> 13
  2. puts dog   #=> siamese
    puts cat   #=> chow chowsiamese
  3. puts a   #=>  111

    When you try to execute the final line, b = a + c, you will get an error. Ruby attempts to calculate the right side of the assignment before assigning the result to b. But because c has not yet been declared, the right-side operation will throw an error:

    NameError: undefined local variable or method `c' for #<0x1001dd2a0>
Exercise: Rename the variables

Copy the following block of code, but change all the variable names to something else, anything else, without changing the program's functionality. Keep the exact number of lines and operations.

One caveat: Do not change the method names (e.g. to_i, to_s, upcase, etc.). We haven't learned about them yet formally, but remember that they are invoked with the dot operator. They look like variables in that they are a label for something (e.g. a block of code that does something). but altering them will almost certainly change the functionality of the program.

variable_x = 90
carton = "a"
xf_var = (carton + variable_x.to_s) + carton      
a = variable_x.to_s.upcase
b = "#{a}#{variable_x}"
xf_var += (variable_x +xf_var.to_i).to_s
puts b + carton
variable_x = variable_x.to_s + "variable_x" + b
puts variable_x
         

Retype the code in a text-editor and change the name of each variable. Do it by hand; no using Find and Replace.

Solution

There's no one answer to this. Just run your version of the code and make sure the output is the same as the original code.

bob = 90
milk = "a"
yvar = (milk + bob.to_s) + milk      
z = bob.to_s.upcase
q = "#{z}#{bob}"
yvar += (bob +yvar.to_i).to_s
puts q + milk + yvar
bob = bob.to_s + "variable_x" + q
puts bob         
      

The output should be:

9090aa90a90
90variable_x9090      
   

Did you mistakenly change "variable_x" to something else?

variable_x = variable_x.to_s + "variable_x" + b

It is a String – hence the quotation marks. So it is interpreted literally, not as a variable.

References, references, references

This next section will test how well you understand that a variable refers to an object. This concept is the key idea to understand about variables.

The identity of objects

To use the coat-check metaphor again, if two people check in the exact same coat, we can think of the coats as being equivalent to each other. But they are two physically different objects. This is the same in Ruby: two objects that have equivalent value can yet be two different objects in physical space (i.e. computer memory).

x = "hello"
y = "hello"

# test x and y using the equality operator
x == y
#=> true

How do you tell if two equivalent Ruby objects occupy different spaces? The most direct way is to use the object_id method, which returns the unique id that the Ruby interpreter gives to every object in memory:

puts x.object_id   #=> 2100050200
puts y.object_id   #=> 2100060720

There you have it. There's not much more to elaborate on at this point, because for all intents and purposes, the values referred to by x and y are equivalent. And for most of the situations we've learned so far, that's all we care about.

A few notes about object_id before moving on:
  1. I can't think of any programming situation, other than trying to demonstrate the details of Ruby's underworks and for debugging extremely confounding programs, in which I've actually needed to use object_id. So don't be concerned that you'll need to keep track of that particular number in a given program.
  2. Certain objects, such as Fixnums (0, 1, 2, 100, 1000, etc.), true, false, and a few others, really do exist as singular objects. So:
    x = 42
    y = 42
    
    puts x.object_id == y.object_id
    #=> true   
    

    The explanation of why this is is not particularly important for our purposes (and frankly, was something I was ignorant of for a long while). But the Ruby docs and Ruby Forum

I don't bring up the concept of object identity just as an academic subject. It will rear its head in virtually every non-simple program that you write, and if you aren't aware of its implications, you will spend a lot of time debugging.

var_two = var_one

What happens when we assign a variable to another (previously declared) variable?

tom = "kitty"
jerry = tom

puts tom
#=> kitty

puts jerry
#=> kitty   

The results shouldn't surprise you. But what happens to jerry when we change what the first variable, tom, refers to?

tom = "kitty"
jerry = tom
tom = "mouse"

puts tom   
#=> mouse

puts jerry   
#=> "kitty"

As I mentioned earlier, don't use integers to test this out, as they are treated differently.

In the first two lines, jerry is assigned to the variable tom. However, when tom is assigned to something else, jerry still points at the object to which tom used to refer.

What happened? Let's look at the first two lines again:

tom = "kitty"
jerry = tom

In the first line, tom is assigned the string "kitty". But how do we describe what jerry is assigned to?

  1. ..."the variable tom"
  2. ..."the object to which tom has been assigned"

The correct interpretation is Option #2. Once we understand that, then the above snippet makes more sense: if jerry is said to be assigned to tom's object, then what happens to tom afterwards shouldn't affect jerry.

Exercise: More of tom and jerry

Note: Remember when we learned that operations don't (typically) alter objects? That will be relevant here.

What do tom and jerry refer to at the end of the following code snippet?

tom = "kitty"
jerry = "kitty"
tom = tom + "kat"
Solution

Try the code out for yourself. Here are the results in irb:

Variables diagram

Review the section in which we learned that non-assignment operations don't affect the values of the objects involved.

...Except when they do, as we will learn in the next section.

In-place operations

OK, one more nuance about variables to drill in.

We'll learn more about methods in the next chapter, but you might remember the upcase string method from the chapter on strings. Just like the addition operation (which is technically also a method), the upcase, downcase, capitalize methods and their like do not modify the strings that invoke them:

quiet_version = "hello there"
loud_version = quiet_version.upcase

puts loud_version
#=> HELLO THERE

puts quiet_version
#=> hello there

However, there is a category of methods that perform in-place operations. That is, they do modify the original invoking object.

Because this is a frequently desired behavior for methods such as upcase, the Ruby authors provide an in-place version of upcase called...upcase! (yes, exclamation marks are allowed at the end of a method name)

quiet_version = "hello there"
loud_version = quiet_version.upcase!

puts loud_version
#=> HELLO THERE

puts quiet_version
#=> HELLO THERE

Note: Not all in-place operations have an exclamation mark. That's just a typical Ruby convention to inform programmers that a given method alters the original object.

Also, not all methods have an in-place version.

String concatenation also has an in-place version. Instead of + or +=, use << to add one string to another, in place:

tom = "kitty"
jerry = tom

tom << "kat"

puts tom 
#=> kittykat

puts jerry   
#=> kittykat

Compare this to the non-in-place version of string addition:

tom = "kitty"
jerry = tom
tom += "kat"

puts jerry   
#=> kitty

puts tom   
#=> kittykat

As we learned previously, the += effectively reassigns tom to a new object. That new object is the sum of what tom used to point to and the string "kat".

But in the in-place version, there is no reassignment. tom's is modified and puts tom reflects the newly modified object. And because jerry pointed to the very same object, it also will reflect the modification.

The big picture of variables and object identity

You may be a little frustrated with tom and jerry by this point. And it may seem that with this in-place operation bit, I've just given you way too much minutiae to memorize.

If this is the case, take a breather. The point is not to memorize the syntax. If tomorrow you get the meaning of << and += mixed up, then that is perfectly OK.

Right now, it is only important that you understand that there is a difference.

And you must also understand why this difference exists.

So this is the time to review the big picture concepts:

  • Variables refer to objects.
  • When one variable is assigned to another variable, then that variable refers to the other's object:
    tom = "kitty"
    jerry = tom
  • Therefore, when one of those variables is reassigned to a different object, this does not change what the other variable refers to:
    tom = tom + "kat"
    puts jerry   
    #=> "kitty"

The reason why I introduce in-place operations – even though we have no application for them yet – is to reinforce the above concepts.

Use logical deduction:

  1. When jerry is assigned to tom, it is assigned tom's object. And so both variables refer to the same object.
  2. When the in-place method << is used on tom's object, it modifies tom's object
  3. Therefore, if jerry points to the same object as tom, then jerry will also be affected by every in-place operation involving tom

A visual guide to variables

If you've gotten this far, then you've read close to a couple thousand words on variables. This is because I've tried to reinforce the same concepts over and over, not because their fundamental workings are so complicated.

But just incase you've lost sight of the big picture, this next section covers the same concepts of variables and their behavior, but with big pictures. At the end are some tricky exercises that you may or may not get right away.

Basic assignment

burrell = "vest"
woz = "Tennis"
Variables diagram

Reassignment to completely different objects

burrell = "vest"
woz = "Tennis"

burrell = "Ohio"
woz = "arc"
         
Variables diagram

Assignment to equivalent, yet different objects

burrell = "vest"
woz = "Tennis"

burrell = "Tennis"
Variables diagram

Assigning one variable to another variable

burrell = "vest"
woz = "Tennis"
burrell = woz
Variables diagram

When two variables point to the same object, reassigning one variable does not affect the other variable

woz = "Tennis"
burrell = woz

woz = "clouds"
Variables diagram

Methods typically act on a copy of the object:

burrell = "Tennis"
woz = "clouds"
burrell = burrell + woz
Variables diagram

Some methods do in-place operations:

woz = "clouds"
burrell = woz
woz << "Paris"
Variables diagram
Exercise: Review time

Time for a quick reinforcement of what we've learned about variables. These are solely meant to be slight brain teasers. You will never have to write such silly arrangements of variable assignments in real world applications.

Determine the value of the variables in each of these code snippets:

  1.  
    foo = "chicken"
    bar = foo
  2.  
    x = 100.0
    y = 100.0
    x += 42.0
  3.  
    a = "One hundred"
    b = a + " and " + a
    
  4.  
    alpha = "star"
    beta = alpha.upcase!
    alpha = beta.downcase
    
  5. (I fooled myself on this one!)
    bob = "uncle-"
    earth = bob 
    earth << bob
    mary = bob.downcase + earth.upcase! + bob.capitalize
    earth << bob
Solution
  1.  
    puts foo  #=> chicken
    puts bar  #=> chicken
    
  2.  
    puts x  #=> 142.0
    puts y  #=> 100.0
    
  3.  
    puts a  #=> One hundred
    puts b  #=> One hundred and One hundred
    
  4.  
    puts alpha  #=> star
    puts beta  #=> STAR
    
  5.  

    This one is a little tricky (and again, don't memorize what happens here, this is not a sequence you would every do in a real application). When earth is assigned to bob's object, both variables point to the same object.

    And so this in-place operation:

    earth << bob

    Is equivalent to this:

    bob << earth  

    – and this:

    bob << bob 

    – because bob and earth refer to the same "uncle-" string object.

    puts bob  #=> UNCLE-UNCLE-UNCLE-UNCLE-
    puts earth  #=> UNCLE-UNCLE-UNCLE-UNCLE-
    puts mary  #=> uncle-uncle-UNCLE-UNCLE-Uncle-uncle-
    

The classic variable swap

The following is an exercise that is simple to solve yet remains a classic litmus test to determine if you understand basic programming concepts.

Exercise: Swap the values in two variables

Given variables a and b, write a script that switches the two values in them so that a contains b's value and vice versa.

Solution

If you thought the solution was too easy, you are probably right. Just use a third placeholder variable:

p = a
a = b
b = p            
         

How can the above solution be made any simpler?

As you learned in this chapter, variables point to locations in memory. However, each variable itself requires allocated memory. So performing the above swap without a third variable is theoretically useful in a situation in which memory is very limited.

Simple arithmetic gives us one possible solution:

a = 42
b = 3   #   a = 42   b = 3

a = b - a   #   a = -39   b = 3
b = b - a   #   a = -39   b = 42
a = b + a   #   a = 3   b = 42
         

The preferred way to do this is through the bitwise XOR operation, something we haven't learned since I skipped covering binary numbers.

Carefully before cleverly

So if this question comes up during an interview, is this two-variable solution the best?

Depends.

First of all, the "clever" solution to this is so well-known that regurgitating it at an interview doesn't prove that you actually understand the underlying arithmetic or bitwise operations.

As with virtually every problem in programming, the real thinking is discerning the requirements of the situation:

  • Is memory at a severe limit? Then yes, then the two-variable algorithm may be the optimal solution.
  • If memory is not a concern, then maybe the interviewer is attempting to see how well you understand the basic architecture of modern CPUs. The operations required to do the two-variable swap may not execute faster than doing the swap with a temporary variable.

First of all, nothing that we cover in this book involves worrying about memory and computer architecture at this level.

But even before getting that deep, the question that you should ask is: what type of values do a and b point to? If the questioner never said that the values are only numbers, then your clever two-variable swap will fail with any non-numerical datatype.

As I've said before, the code in this book is meant neither to be the most clever or the fastest for the machine to execute. I've aimed for readability and comprehension first. And in a high-level language like Ruby – and in the kinds of programming use-cases covered in this book – what we might lose in processing speed (we're talking about in the magnitude of hundredths of seconds), we'll gain in human speed.

When learning how to program – and while doing it for "real" – you'll value the hours you save in trying to comprehend too-clever-but-confusing code over those hundredths of machine seconds.

In conclusion...

Unfortunately, I spent so much time rewriting this chapter that I didn't have the time to include a proper conclusion. Skip back to the chapter's overview. If you can understand that without too much problem, then you're good to go on.

If not, don't lose hope. Explaining abstract concepts are not my strength. But there are many great programmers and writers out there who may be better for making things click for you. Check out the Resources section as a place to start.