Core Programming Elements - Computer Science Programming Basics in Ruby (2013)

Computer Science Programming Basics in Ruby (2013)

Chapter 3. Core Programming Elements

IN THIS CHAPTER

§ How to install Ruby and save files

§ Defining variables

§ Data classes (types):

§ Integer

§ Float

§ String

§ Boolean

§ Input and output

§ Common programming errors

3.1 Introduction

The first chapter introduced computer science basics, focusing on the concept of algorithms. The second chapter discussed the basic components of a computer. Now it is time to introduce core programming elements, the most basic tools of programming languages. We will show examples of this using the Ruby programming language. These include constants and variables of various data types and the process of input and output. Also, we will explain common programming errors encountered when using the information covered in this chapter.

3.2 Getting Started

How to Install Ruby

The time has come for you to begin writing simple programs. Before you can do that, you need to install Ruby. This is explained in Appendix B at the back of the book.

How to Save Programs

The next thing to learn is how to save your work. When writing a computer program (informally called code), it is often important to be able to save it as a plain text file, which can be opened and used later.

To save a program, you must first open a piece of software that allows you to create, save, and edit text files. These programs are called text editors, and examples include Notepad, Scite (included in the one-click installation of Ruby), and many others we discuss in Appendix C. For more advanced editors, you may want to look into vim and emacs. There is also a version of the integrated development environment (IDE) Eclipse that works with Ruby. Eclipse includes a plain text editor. Once a text editor is open, be sure it is set to save as an unformatted text file (FileName.txt). Most word processors, such as Word, add special characters for document formatting, so these should not be used for writing programs. If special characters are turned off by saving the document as a plain text file (.txt), you can use various word processing programs, such as Word.

Now you are ready to write and save programs.

GEM OF WISDOM

Plain text files (sometimes seen with the extension .txt) are stored as a simple sequence of characters in memory. For example, files created with Notepad on Windows are plain text files. Try to open a Microsoft Word document in Notepad and observe the results. Non-plain text files are commonly called binary files.

3.3 What Is a Variable?

A variable is a piece of data attached to a name. In algebra, a variable like x in the equation x = y + 2 indicates that x and y can take on many different values. In most programming languages, variables are defined just as in algebra and can be assigned different values at different times. In a computer, they refer to a location in memory. Although this is a simple concept, variables are the heart of almost every program you write. The Pythagorean theorem is shown in Figure 3-1, and it uses three variables: A, B, and C.

Pythagorean theorem

Figure 3-1. Pythagorean theorem

A, B, and C are the variable names, and they each represent a number. To create a variable in Ruby, simply use the format variable_name = value, where variable_name is the name of your variable and value is the value you would like it to have. The equal sign (=) behaves differently in Ruby from the way it does in algebra. In Ruby, = means “assign the RHS (righthand side) to the variables in the (LHS) lefthand side.” The code snippet y = x + 2 means compute the value of x + 2 and store the result into the variable y. In algebra, y = x + 2 simply explains a relationship between variables x and y.

An easy way to test things in Ruby is with irb (once Ruby is properly installed), the Interactive Ruby Shell. Enter irb from a command prompt; you can see that it is very easy to create variables. The following example shows several variables being created and assigned values.

irb(main):001:0> a = 4

=> 4

irb(main):002:0> b = 3

=> 3

irb(main):003:0> c = 5

=> 5

This example creates three variables named a, b, and c and initializes their data to 4, 3, and 5, respectively. While these variables were given simple names, variables can be given almost any name you wish. However, it is poor style to create variable names that are unclear (e.g., x1, x2, zxy). Variable names should explain the data they represent. When naming variables, avoid special characters (%, $, #, @, etc.) because these characters often carry specific meaning. Also note that variables cannot begin with integers. Ruby is case-sensitive, so myName is different from myname. Variable names should start with a lowercase letter, while constants (to be explained shortly) start with an uppercase letter. Additionally, people who write programs tend to be concerned about readability. Ruby users tend to name long variables with underscores (_). For example, instead of naming a variable primenumber or primeNumber, in Ruby it is named prime_number, although the former are both acceptable. This is simply a stylistic pattern, not a requirement.

A variable refers to a section of memory that has been set aside to hold whatever data you specify. Every location in memory has an address that can be accessed by a program. Instead of asking you to write a program to store the value xx into memory location yy, most programming languages allow you to identify a variable and assign a value to it. The picture in Figure 3-2 further illustrates the concept of variables and memory. variable_1 has a value of 5 stored at location 10, while variable_2 is stored at location 20 with a value of 8.

Memory diagram

Figure 3-2. Memory diagram

Variables store different types of data, not just numbers. They can hold user-created data, words, numbers, and more. Depending on what type of data the variable is holding, it will have a different data type.

Constants: Variables That Never Change

If a variable does not change, it is called a constant. For example, 3.14159 is commonly understood as π or simply pi. By creating a variable called PI and never changing it, that is, making the variable into a constant, one can use PI throughout the program instead of repeatedly typing the numeral string. By following this convention, fewer errors are likely to occur as this value is only entered one time, namely when the constant value is assigned. Typing the value of π multiple times might lead to typing errors. Furthermore, readability is enhanced. For example, it is far more intuitive to describe an algorithm that computes the area of a circle as one that squares the radius and multiplies the result by PI than one that squares the radius and multiplies by 3.14159. As noted before, constant names start with a capital letter, and by convention are all uppercase.

Data Types

Data types indicate the nature of the variable and the range of valid values for that variable. Data types also indicate how much memory to allocate for each type of data. It is important to know that specific types of data have specific sizes in memory. As will be evident later, data types also define the meanings of allowed operators.

Ruby refers to data types as classes. In Ruby, as in other object-oriented languages, a class is a description of objects. A detailed discussion of objects takes place in Chapter 9. For now, the relevant Ruby classes are Fixnum, Bignum, Float, Boolean, and String.

Integer

An integer is defined as any natural number (0, 1, 2, . . . ) or its negative (0, -1, -2, . . . ). The integer data type is simply a subset of the infinite mathematical integers. In a 32-bit system, integers range in value from -2,147,483,648 to 2,147,483,647. Note that if the number of bits used differs, so will the range.

Consider the following:

irb(main):01:0> x = 5

=> 5

When we type in x = 5, we are doing two things. First, we are creating a variable x that stores an integer of the class Fixnum. Second, we are assigning the value of 5 to x. In some languages, variables must be explicitly assigned to a type before they are used. In Ruby, the class of the variable is either explicitly stated or inferred in every assignment of the variable. In this case, Ruby infers the Fixnum class.

Imagine creating a huge number, out of the range of supported integers:

irb(main):02:0> x = 1_000_000_000_000_000

=> 1000000000000000

The Bignum class is used by Ruby for very large integers not contained within the previously stated integer range. This class enables the creation of integers outside the range of those possible in Fixnum.

GEM OF WISDOM

Ruby (along with many other programming languages) does not allow commas in numbers. Underscores, however, can be used in their place. Use an underscore in place of a comma if you need to mark your thousands so that larger numbers are more readable.

Float

Simply put, a float is a decimal number. In Ruby, floats can be defined either with numbers containing a decimal place or in scientific notation.

irb(main):001:0> x = 5.0

=> 5.0

irb(main):002:0> x = -3.14159

=> -3.14159

irb(main):003:0> x = 3.5e2

=> 350.0

Similar to the integers example, when we execute x = 3.14159, we create a variable x that stores a float. We then assign the value of 3.14159 to x. Negative values are expressed with a leading negative sign (-).

For scientific notation, values are expressed in powers of 10 following the symbol e. Hence, 3.5e2 indicates 3.5 × 102 = 350. Very small numbers can be represented as negative powers of 10. You may find that the designation real is used in other languages to denote float.

Strings

Strings are simply one or more characters surrounded by quotes. Both double quotes (") and single quotes (') can be used, but if you need to use a single quote inside your string, you must bound your string with double quotes (e.g., “The computer said, ‘Hello World!’ ”).

irb(main):001:0> x = "hello world"

=> hello world

irb(main):002:0> y = "hello, 'world'"

=> hello, 'world'

To store characters (such as letters and symbols), a character encoding scheme, as discussed in Chapter 2, is used. For example, to encode the English alphabet, we need to represent 26 uppercase letters, 26 lowercase letters, and a variety of special characters. Using eight bits (one byte) per character allows us to encode 256 characters, a number more than sufficient to represent English. Each character is assigned a unique number from 0 to 255.

Booleans

Booleans can hold only two different values: true and false. They are sometimes referred to as flags.

irb(main):001:0> x = true

=> true

irb(main):002:0> y = false

=> false

3.4 Basic Arithmetic Operators

Now that some of the classes that define various data types have been introduced, what can you do with them? Like all other programming languages, Ruby is able to perform many mathematical operations. In Table 3-1, we illustrate how to use Ruby as a calculator.

Table 3-1. Basic arithmetic operators

Symbol

Operation

Example

+

Addition

x = 6 + 2

8

-

Subtraction

x = 3 - 2

1

*

Multiplication

x = 5 * 2

10

/

Division

x = 16/8

2

%

Modulus

x = 5 % 3

2

**

Power

x = 2 ** 4

16

All of the operators listed are binary operators, meaning the operator has two operands. For example, the command A + B is a binary operation, where A and B are the two operands and + is the operator.

When typing mathematical operations in the Ruby interpreter, the order of operations is taken into account. To change the order of operations, use parentheses. Try typing the following in the command line:

irb(main):001:0> x = 10 + 4 / 2

What result did you get? Now try entering the following into the prompt:

irb(main):001:0> x = (10 + 4) / 2

Most of the operators should look familiar. The one that might not is the modulus operator (%). The purpose of this operator is to find the remainder produced by dividing two numbers. For example, 4 modulo 2, abbreviated 4 mod 2, would produce the result 0. This is because 4 / 2 is exactly 2. On the other hand, 2 mod 4 produces a result of 2. This is because 2 / 4 is 0, with a remainder of 2. Let’s try to solve a few easy problems using the modulus operator.

1. Using the mod operator, determine if a number is even. This should be fairly easy. We know that n is even if, when divided by 2, it produces a remainder of 0. So, if n mod 2 equals 0, then n is even.

2. irb(main):001:0> x = 5%2

3. => 1

4. irb(main):002:0> x = 6%2

=> 0

5. Given a number as input, determine if the number is prime. That is, the given number must not have any factors other than 1 and itself. For example, 1, 3, 5, and 7 are prime numbers. The number 2 is the only even prime number since all other even numbers have 2 as a factor. Likewise, for example, the number 9 is also not prime since 3 divides it evenly. As an aside, finding prime numbers is one of the classic problems used to teach any programming language. Furthermore, prime numbers play a significant role in information security. For example, SSL (Secure Sockets Layer), which is what you use when you go to a website and type “https”, uses an algorithm called public key encryption. This algorithm relies heavily on prime numbers. The intuition behind the use of prime numbers is that for long numbers (those that comprise hundreds of digits) it takes a computer a very long time to determine their factors. Thus, it is safe to publicly present these numbers.

Although we may not be able to program this yet, we can come up with an algorithm to solve this problem. Namely, we can do the following:

If n is equal to 2, then n is prime. Otherwise, take each number x in the range 2 to n – 1. If n mod x is equal to 0 for any of these numbers x, then the number n is not prime. If n mod x is not equal to 0 for every number in the range 2 to n – 1, then the number n is prime.

Note that this is not an efficient algorithm, but it is correct. Can you think of a more efficient approach? Hint: Do we really need to check all the way up to n – 1?

GEM OF WISDOM

Components of a mathematical expression can usually be broken into operands and operators. For example, in the expression 2 + 3, 2 and 3 are operands, and + is the operator. Operands are usually values, and operators are the actions to be performed.

Now that we have discussed the basics, we will describe some slightly more advanced operations. Ruby has many built-in modules; a module is simply a group of methods for a particular domain. There are methods that accomplish many different tasks. We will discuss methods in great detail starting with Chapter 8. For example, Ruby has a built-in module for mathematics, the Math module. Table 3-2 lists some of the more advanced mathematical functions in Ruby. These functions are referred to as methods.

Table 3-2. Advanced arithmetic operations

Method

Operation

Example

Result

sqrt()

Square root

x = Math.sqrt(9)

3.0

sin()

Sine

x = Math.sin(0)

0.0

cos()

Cosine

x = Math.cos(0)

1.0

tan()

Tangent

x = Math.tan(0.7854)

1.0

log()

Natural log (ln)

x = Math.log(5)

1.609

log10()

Log (base 10)

x = Math.log10(10)

1.0

When trying to perform these operations, we specify the Math module, followed by a period (.), then the method (type of operation). For example, to find the square root of 16, type:

irb(main):001:0> x = Math.sqrt(16)

=> 4.0

To appreciate the power of the Math module and understand the order of operations in Ruby, try creating a program that performs the following operation:

Advanced arithmetic operations

The result should look similar to this:

irb(main):001:0> x = (5 + Math.sqrt(9)) / 2

=> 4.0

Make sure you obtain 4.0 as a result. If you do not, try again. With computers there is no point in repeating failure precisely. Often, novice programmers state: “Well, I tried it 500 times!” If no input was changed, rest assured that no output will change either. So change something and then try again! Be patient, check things carefully, and keep working until the result is 4.0. If you obtained 4.0 the first time, try some variations (e.g., misspell the word Math), and become familiar with some error messages. It is important to start being methodical about implementing programs on a computer on the very first day. Take your time, go slowly, and think about everything you enter. Sometimes with programming languages even the smallest detail can be the difference between success and failure.

GEM OF WISDOM

Ruby comes with many built-in functions called methods to make your life easier. Table 3-2 shows a few of them. Without a square root (i.e., sqrt()) function you would need to write a program to compute the square root by repeated division. For any programming language, make sure you learn about all the built-in functions, as they can save you significant time.

3.5 Input and Output

Output Using Variables

Displaying text in Ruby is simple. Try entering the following at the command prompt:

irb(main):001:0> puts "Hello World"

The puts method (short for outPUT a String) simply displays a string on the screen. Notice the string is contained in quotation marks; otherwise, both Hello and World would be interpreted as variables. Variables are displayed on the screen using similar syntax, except without quotation marks:

irb(main):002:0> text = "Hello World"

=> "Hello World"

irb(main):003:0> puts text

=> Hello World

This example stores the string “Hello World” in a variable named text and then displays the value stored in the text variable using the puts method. This method is not limited to strings and can be used with other classes including integers, floats, and Booleans.

The use of classes to define data types means a variety of methods can be done for each type. For example, x.length indicates the size of a string when x is defined as a string.

Display User Input

Displaying user input in Ruby is almost as easy as displaying output. Try entering the following in the command prompt:

irb(main):001:0> age_input = gets

The gets method (short for GET a String from the user) stops the program and waits for the user to type some text and then press Enter. The text typed by the user will be stored as a string in a variable called age_input. Due to the nature of the gets method, the value stored in age_input will be a string, but you need an integer if you wish to mathematically manipulate it. We create another variable age and set it equal to the integer value of the user’s input by converting the string age_input to an integer. This is done by issuing the following command:

irb(main):002:0> age = age_input.to_i

The method to_i converts age_input to an integer.

Basic Programs

Now that you have learned how to display text and request user input, you can develop a program that calculates the area of a rectangle. Try using the problem-solving approach discussed in Chapter 1 to create this program.

Step 1: Understanding the Problem

Ask yourself key questions that must be answered to properly design the program:

§ How do you find the area of a rectangle?

§ How many variables do you need to represent that area?

§ What data type do the variables need to be?

Step 2: Write Out the Problem in Plain Language

Before writing out the problem, remember that the input method stores user input as strings, so we need to convert the lengths (which are stored as strings) to integers before performing mathematical operations with them.

1. Ask for the length.

2. Store the length.

3. Ask for the width.

4. Store the width.

5. Convert the length to an integer.

6. Convert the width to an integer.

7. Calculate rectangle area (area = length × width).

8. Display area.

The equation for the area of a rectangle is the product of its length and width. Although it does not affect this equation, remember that the rules for order of operations apply in Ruby. To change the order of operations, use parentheses.

Step 3: Rewrite the Plain Language into Code

See Example 3-1. In the figure, as with all other program illustrations, the line numbers are not part of the code; they are added strictly for explanatory purposes. In line 8, we are printing an integer represented in the variable area. This differs from the printing of character strings in lines 1 and 3. Ruby automatically determines the data type, if it can. This is called dynamic or “duck” typing. This functionality is helpful; however, as you will see, automatic type determination can introduce problems. In this example, though, it helps in that puts area is mapped to puts area.to_s. Note that generally speaking, the .to_* method is a type conversion operation where the target type is represented by the * (a wildcard). For example, you have now seen conversion to integer (to_i) and to string (to_s).

Example 3-1. Code with comments

1 puts "What is the length?" # Ask for the length

2 length_input = gets # Stores the length

3 puts "What is the width?" # Ask for the width

4 width_input = gets # Stores the width

5 length = length_input.to_i # Convert length to integer

6 width = width_input.to_i # Convert width to integer

7 area = length * width # Calculate rectangle area

8 puts area # Display area

Step 4: Test the Code in the Computer

Similarly those who are adventurous can type the code lines from step 3 into a file and run the code from there. For those who are unsure, no worries; we address files later on. For now, simply see if you get the desired results; if not, make sure you typed everything correctly.

If it works, congratulations! You have just created a program that calculates the area of a rectangle.

3.6 Common Programming Errors

Syntax Errors

As a programmer, it is important for you to become comfortable with error messages. Even the most experienced computer programmers will have a typo or forget a quotation mark regularly. When you first start, error messages will seem incomprehensible. Later, they will help you fix the problem quickly.

GEM OF WISDOM

When you get an error message, do not just try the program again. This may work with your toaster, where you just unplug it and plug it back in, but with software development, it rarely, if ever, works. Figure out the problem, make a correction, and then see if it works.

Try the following command in irb:

irb(main):001:0> x = 1 + "hello"

TypeError: String can't be coerced into Fixnum

from (irb):1:in '+'

from (irb):1

Syntax errors refer to code that Ruby cannot actually execute. Since it does not make sense for Ruby to add an integer to a string, it stops execution and informs you of the location where it had to stop.

Let’s move on to a more realistic example. When entering strings, we are likely to forget quotes once in a while.

irb(main):002:0> x = hello

NameError: undefined local variable ormethod 'hello' for

main:Object

from (irb):2

Wait! Why does the Ruby interpreter not just tell us that we forgot quotes? Ruby must guess what we intended. All Ruby knows is that we tried assigning hello to x. Since any string not contained in quotes is the name of a variable or a command, Ruby tries to find the value stored in a variable named hello. Since Ruby cannot find any variable named hello, it prints an error. It is important to start viewing code in terms of how Ruby might interpret it, but this is a skill that will take time to develop. Meanwhile, realize that errors will happen, and expect to be learning from them.

Logic Errors

Errors that Ruby cannot catch are logic errors. They are problems with a program that cause it to return the wrong or undesired result. For example, if you are trying to find the area of a rectangle, but you multiplied the height by itself instead of by the width, that would be a logic error.

The Ruby interpreter, the program that executes the Ruby instructions input by the user, does not try to guess that we meant to use a different variable. Logic errors are often much harder to trace than syntax errors because of this. To avoid them, it is often useful to look at your program and “play computer.” That is, pretend that you are the Ruby interpreter, and follow the steps that will be implemented as the program is executed. From this point on, we’ll use the words Ruby and Ruby interpreter interchangeably.

GEM OF WISDOM

Truncation refers to limiting the number of significant digits by discarding extra digits. Unlike rounding, truncation simply throws the extra digits away.

A more common error involves integer division. Arithmetic expressions that operate on integers will always evaluate to integer form. This happens even with division, where the result would otherwise be a decimal. Ruby does this by truncating the arithmetic result (chopping off the decimal place and all the digits to the right of it). For example, 5 divided by 2 will evaluate to 2 instead of to 2.5.

irb(main):003:0> 5 / 2

=> 2

If you get a wrong value using this computation in a program, then your final result will likely be erroneous, too.

3.7 Mixing Data Types

Ruby will always try to remain in the same data type as its operands. For example, if the integers 2 and 3 are added in Ruby, the result will also be an integer.

irb(main):001:0> 2 + 3

=> 5

Likewise, when adding two floats, the result will also be a float.

irb(main):002:0> 2.0 + 3.0

=> 5.0

When Ruby encounters two operands of different data types, it will convert them to match where possible.

irb(main):003:0> 2 + 3.0

=> 5.0

The issue of dividing integers like 5 / 2 can finally be resolved. We are able to force Ruby to convert integer expressions into float expressions. Simply throw a float into the mix.

irb(main):004:0> 1.0 * 5 / 2

=> 2.5

Of course, it is sometimes impossible to convert the data types to match. In this case, Ruby will output a TypeError.

irb(main):005:0> x = 1 + "hello"

TypeError: String can't be coerced into Fixnum

from (irb):4:in '+'

from (irb):4

3.8 Summary

We discussed classes, variables, constants, and key data types such as strings and integers. At this point, you should know how to create a variable in Ruby and how to assign values to your newly created variable.

3.8.1 Key Concepts

§ Ruby programs can be created/edited in any text editor (as defined previously), so long as the editor can save files as plain text. It is highly recommended that you use an editor suited for writing programs.

§ When naming variables, it is a common practice to separate each word in a variable name with an underscore (_).

§ Programming languages use various data types to perform various operations. Ruby uses many data types, including integer (Fixnum and Bignum), float, string, and Boolean.

§ It is important to understand the syntax behind simple mathematical operators. Mathematical operators will often be used when programming. Pay attention to the order of operation.

§ To output information onto the screen, use the puts command. To input information from the user, use the gets command.

§ The three types of programming errors are syntax errors, logic errors, and type errors. Type errors often arise from mixing data types that cannot be mixed, such as integers and strings.

§ Conversion errors are a subset of type errors.

3.8.2 Key Definitions

§ Variable: A piece of data attached to a name, class, and memory location.

§ Instantiate: In this simple initial presentation, creating a variable. See Chapter 9 for more information.

§ Constant: A variable that is set once and never changed.

§ Initialize: To assign a value to a variable when the variable is created.

§ Data class: Information about the variable that defines the possible values a variable can have and what operations can be performed using it. A more complete description is presented in Chapter 8.

§ Integer: A class that defines a whole number. Ruby recognizes two classes:

§ Fixnum: An integer with a limited range. In 32-bit systems, the range –2,147,483,648 to 2,147,483,647.

§ Bignum: An integer outside the range of Fixnum.

§ Float: A class that defines a decimal number.

§ String: A class that defines a sequence of characters.

§ Boolean: A class that defines a value of either true or false.

§ Syntax errors: Errors produced by incorrect syntax for the programming language.

§ Logic errors: Errors produced by bad logic.

§ Type errors: Errors produced by mixing data types that cannot be mixed.

3.9 Exercises

1. You saw that Ruby does not allow addition of floats and strings. For example:

2. irb(main):005:0> 1.1 + "string"

3. TypeError: String can't be coerced into Float

4. from /Users/leland/.irbrc:73(irb):1:in '+'

from (irb):14

What type combinations does Ruby allow to be added?

5. Using irb, initialize three variables, x, y, and z, each to some number less than 10. Design an equation with these variables using at least one multiplication, one division, and one addition or subtraction element. Have the computer do it once without parentheses, and then add parentheses to the equation and try it again. Are the answers the same? If not, why not?

6. Earlier in the chapter, we saw the following:

7. irb(main):001:0> 1.0 * 5 / 2

=> 2.5

Now, try typing the following code into irb:

irb(main):002:0> 5 / 2*1.0

This should have produced a value of 2.0. Why does it produce the value 2.0, and not 2.5, like we saw earlier?

8. Write the expected value of x after both lines are executed.

a. irb(main):001:0> x = 9

b. irb(main):002:0> x = x/2

c. irb(main):003:0> x = 9

d. irb(main):004:0> x = x/2.0

9. What is the expected result of c for each code group?

a. irb(main):001:0> a = Math.sqrt(9)

b. irb(main):002:0> b = 2

c. irb(main):003:0> c = a/b

d. irb(main):001:0> a = 5

e. irb(main):002:0> b = 2

f. irb(main):003:0> c = a/b

10.Suppose a program finds the average temperature for a given year. A user of the program is prompted to enter the average temperature values for each season of the year: winter, spring, summer, and fall. The program stores these values as floats in variables temp_winter, temp_spring, temp_summer, and temp_fall, respectively. The final result is stored in the variable avg_temp. The program calculates the average temperature with the following line:

avg_temp = temp_winter + temp_spring + temp_summer + temp_f/4

However, when the program runs, the value of avg_temp is always incorrect. Briefly describe what is wrong with this line and what changes you would make to correct this error.

11.What is the difference between logic and syntax errors? Give an example of each.