Chapter 3: Methods

3.1 Floating-point and Fixnum

In the last chapter we had some problems dealing with numbers that were not integers. We worked around the problem by measuring percentages instead of fractions, but a more general solution is to use floating-point numbers, which can represent fractions as well as integers. In Ruby, the floating-point type is called Float.

You can assign floating-point values to variables using very much the same syntax we used for the other types. For example:

square_root_of_2 = 1.41421

When assigning a floating-point value, there must be at least one digit following the decimal point. Ruby reports an error when it tries to parse the first line in the following example:

three = 3.  # confusion
three = 3.0 # happiness

Although floating-point numbers are useful, they are often a source of confusion because there seems to be an overlap between integers and floating-point numbers. For example, if you have the value 1, is that an integer, a floating-point number, or both?

Strictly speaking, Ruby distinguishes the integer value 1 from the floating-point value 1.0, even though they seem to be the same number. They belong to different types. The value 1 is a Fixnum type of integer, and the value 1.0 is a Float.

What is a Fixnum? Explain Fixnum and Bignum here.

It is easy to forget that an integer and a floating-point value are not the same type, especially because there are places where Ruby will automatically convert from one type to another. For example, in the statement

y = 1 / 3.0

the Fixnum 1 is automatically converted to a Float before the division is performed.

This leniency is convenient, but it can cause problems; for example:

y = 1 / 3

You might expect the variable y to be given the value 0.3333333333, which is a legal floating-point value, but in fact it will get the value 0. The reason is that the expression on the right appears to be the ratio of two integers, so Ruby does integer division, which yields the integer value 0.

One way to solve this problem (once you figure out what it is) is to make the right-hand side a floating-point expression:

y = 1.0 / 3.0

This sets y to 0.3333333333, as expected.

All the operations we have seen so far—addition, subtraction, multiplication, and division—also work on floating-point values, although you might be interested to know that the underlying mechanism is completely different. In fact, most processors have special hardware just for performing floating-point operations.

3.2 Converting from Float to Integer and back

As I mentioned, Ruby converts an Integer to a Float automatically if necessary, because no information is lost in the translation. On the other hand, going from a Float to an Integer requires rounding off. Ruby doesn’t perform this operation automatically, in order to make sure that you, as the programmer, are aware of the loss of the fractional part of the number.

To convert a floating-point value to an integer, we use the to_i method. Think of to_i as meaning “to integer.” The syntax is interesting: you put the dot operator immediately after the value and then the method you wish to invoke—to_i—immediately after the dot operator. It’s easier than it sounds.

pi = 3.14159
x = pi.to_i

Notice the variable pi in the second line above appears to be followed by a decimal point. This is the dot operator. (It’s just a period on your keyboard.) The dot operator in effect says, “Send the message to_i to the value of pi.” The to_i method has the effect of converting what precedes it into an integer, so x gets the value 3. The value of the variable pi, however, does not change; it remains 3.14159.

The to_i method (or any method after the dot operator) takes precedence over arithmetic operations. So, in the following example, the value of pi gets converted to an integer first, and the result x is 60.0, not 62.0.

x = 20.0 * pi.to_i

Converting to a positive integer always rounds down, even if the fraction part is 0.99999999. Conversely, converting a negative number always rounds up. For example, 5.7 becomes 5 and -5.7 becomes -5. In other words, the floating-point number, whether positive or negative, is always rounded in the direction of zero. Or in even simpler words, the digits after the decimal point are always chopped off when converting to integer.

You may be wondering now how to convert an integer value to a floating-point value. Just as to_i converts a value to an integer, to_f (“to float”) converts a value to floating-point. The conversion takes place before any arithmetic operations.

three = 3
x = 1 / three.to_f

Here the value of three is converted to floating-point 3.0 before the division is performed. Because the numerator and denominator are not both integers, Ruby performs floating-point division. The result x is 0.3333333333.

Experiment with to_i and to_f in irb. You will find that invoking to_i on an integer is harmless. Likewise, invoking to_f on a floating-point value is also harmless.

You may be surprised to learn that to_i and to_f can be performed directly on plain old numbers, not just variables. Just place the dot operator directly after the number, even if the number has a decimal point. Try the following in irb:

irb(main):001:0> 3.to_f
3.0
irb(main):002:0> 3.14159.to_i
3
irb(main):003:0> 3.14159.to_i.to_f
3.0

Notice in the last experiment that you can chain (chaining: sending a sequence of messages, often separated by dot operators) several messages. The messages are performed left to right. First, 3.14159 is converted to the integer 3, then the integer 3 is converted to the floating-point value 3.0.

Now I bet you can figure out why floating-point numbers must have at least one digit after the decimal point. If, for example, your code were to contain

x = 3. # error

Ruby would not know whether the 3 is followed by a decimal point or a dot operator. If there were some numerals after the 3., Ruby would know that the point is a decimal point. If there were some letters after the 3., Ruby would know that the point is a dot operator.

3.3 Math methods

You may have noticed that I’ve tossed around the word “method” in the last section. Now we’ll learn what methods are.

In mathematics, you have probably seen functions like sin and log, and you have learned to evaluate expressions like sin(π/2) and log(1/x). First, you evaluate the expression in parentheses, which is called the argument of the function. For example, π/2 is approximately 1.571, and 1/x is 0.1 (assuming that x is 10).

Then you can evaluate the function itself, either by looking it up in a table or by performing various computations. The sin of 1.571 is close to 1, and the log of 0.1 is -1 (assuming that log indicates the logarithm base 10).

This process can be applied repeatedly to evaluate more complicated expressions like log(1/sin(π/2)). First we evaluate the argument of the innermost function, then evaluate the function, and so on.

Ruby provides a set of built-in functions that includes most of the mathematical operations you can think of. These functions are called methods. Most math methods are in the Math module, a module being a collection of methods.

The math methods are invoked using a syntax that is somewhat similar to what we have already seen:

irb(main):001:0> Math.sqrt(17)
4.123105626
irb(main):002:0> Math.sqrt(17.0)
4.123105626
irb(main):003:0> angle = 1.571
1.571
irb(main):004:0> Math.sin(angle)
0.9999999793

The first couple examples return the square root of 17. The final example finds the sine of 1.571, which is the value of the variable angle. The Math module assumes that the values you use with sin and the other trigonometric functions (cos, tan) are in radians. To convert from degrees to radians, you can divide by 360 and multiply by 2π. Conveniently, the Math module provides π as a built-in value:

irb(main):005:0> Math::PI
3.141592654
irb(main):006:0> degrees = 90
90
irb(main):007:0> radians = degrees * 2 * Math::PI / 360
1.570796327
irb(main):008:0> Math.sin(radians)
1.0

Notice that PI is in all capital letters. It is called a constant because its value never changes. A constant is a “variable” whose value does not vary. (How’s that for an oxymoron?) In Ruby, the name of a constant must begin with a capital letter. Although it’s not necessary, it is customary for the name to be all capitalized. As you can see, the Math module’s built-in PI is all capitalized; Ruby does not recognize Pi, pi, or pie.

x = (Math::PI * 20.0).round
#puts x

3.4 Composition

#!/usr/bin/env ruby

angle = 1.5
x = Math.cos(angle + Math::PI / 2)
#puts x

########################################

x = Math.exp(Math.log(10.0))
#puts x

3.5 Adding new methods

#!/usr/bin/env ruby

=begin
def NAME
  STATEMENTS
end

def NAME ( LIST OF PARAMETERS )
  STATEMENTS
end
=end

########################################

def new_line
  puts
end

########################################

puts "First line."
new_line
puts "Second line."

########################################

puts "First line."
new_line
new_line
new_line
puts "Second line."

########################################

def three_line
  new_line; new_line; new_line;
end

puts "First line."
three_line
puts "Second line."

3.6 Classes and methods

#!/usr/bin/env ruby

def new_line
  puts
end

def three_line
  new_line; new_line; new_line;
end

puts "First line."
three_line
puts "Second line."

########################################

puts 2 ** 10

3.7 Programs with multiple methods

3.8 Parameters and arguments

#!/usr/bin/env ruby

def print_twice(phil)
  puts phil
  puts phil
end

########################################

print_twice("Don't make me say this twice!")

########################################
argument = "Never say never."
print_twice(argument)

3.9 Stack diagrams

3.10 Methods with multiple parameters

#!/usr/bin/env ruby

def print_time(hour, minute)
  print hour
  print ":"
  puts minute
end

########################################

print_time(11, 59)

3.11 Methods with results

3.12 Glossary

3.13 Exercises

GNU Free Documentation License Valid HTML Valid CSS Built with BBEdit
Friendly links for Google “juice”