Intro to Functions

Posted by Berin Loritsch Fri, 22 Aug 2008 11:58:00 GMT

When you are just writing quick scripts, you can use Ruby all you want and be happy. However, there comes a point where you have to do the same thing in a bunch of places. Functions are a way to organize the logic in your code so that you can re-use it in more than one place. I’ll introduce how to do math at the beginning, but functions aren’t only for numbers as we will show later.

Doing Some Math

As long as you are working with numbers, you will have to remember some symbols. In your math text books you will see symbols that just don’t exist on keyboards and requires different key combination to make them show up. The good news is that the conventions for replacing mathematical symbols in code is pretty standard across languages. You only have to learn them once, which helps.

  • + addition
  • - subtraction
  • * multiplication
  • / division
  • % modulus
  • ^ exponent
  • () group expressions

Math expressions are performed in algebraic order. In short, that means that expressions are evaluated in the reverse order from what I listed. Parentheses first, exponents next, then multiplication, division and modulus, finally addition and subtraction. Just to make it clear, look at the following code:

puts 4 + 5 * 6
# 34

puts (4 + 5) * 6
# 54

puts 4 + (5 * 6)
# 34

It’s a good habit to use parentheses to make things clearer. There’s a few more symbols that allow you to do bit manipulation, but then I have to explain the math behind it. Let’s focus on this level of math for now. Let’s say we want to do a little trigonometry and calculate the area of a circle. The mathematical formula for the area of a circle is πr2. So how do we get a hold of the value of π? There is a Ruby module called Math that has the value of π and other more advanced functions.

Ok, so how does the expression look like in Ruby?

radius = 5

puts Math::PI * (radius ^ 2)
# 15.707963267949

I added the parentheses to make it clearer that the exponent (raising to the power of two) comes first. So what if we wanted to reuse this function anywhere? We would have to create a function to do it. It’s pretty easy, and you will use the same construct in another post when we talk about creating our own methods. Let’s create our function:

def area radius
    Math::PI * (radius ^ 2)
end

So what’s going on here? The word def is a Ruby keyword that tells Ruby that you are creating a function. After that, is the name of the function. Finally we have the list of parameters. A parameter is a name we give to a value that you pass to the function. Basically, the function is going to do something with that value—even though it doesn’t know what the value is first. The next line bears some explaining.

Functions can return a value, which is usually their whole point. However, we don’t see any words that say “return this”. It’s probably the most unintuitive thing you’ll run into with Ruby, but the last expression in a function is the value that’s returned. It’s a carryover from Smalltalk, and once you understand that it becomes a little more understandable. If we had one more line that just had the number 2 on it, then the function would always return the number 2—which is wrong for what we want. What some people do to make things a bit clearer is to use the keyword return . That keyword is designed for letting you leave a method early for some cases, but it works just as well. It’s probably not a bad habit as other languages require you to use it. The method would now look like this:

def area radius
    return Math::PI * (radius ^ 2)
end

The keyword end is something we saw already when we were doing loops in the last lesson. This keyword is used to end any block, so you will use it a lot.

Not All Functions Are for Math

I introduced functions with math because that’s where the idea came from. But most problems don’t require the use of heavy math. Ruby isn’t designed to be a math engine anyway. Just for fun, let’s create a function that will turn a number into words—Japanese words to be exact. It’s only fitting as Ruby came from Japan after all. Just to save us some work, we’ll limit ourselves to the range from 0 to 99. To do that we need to use an if statement. The if statement let’s us do something if it is true, but skips the code inside if it is not true. We also want to raise an issue so that the calling code knows that they asked something we can’t deliver. The keyword is raise , which is rather convenient. You can “raise” any object, but we will just use a string. The code looks like this:

if not (0..99).include? number
    raise "We can only translate numbers between 0 and 99" 
end

I’ll include the solution below, and just expound on things in comments. Your job is to expand the method to do up to 999, or to change it to another language. I’m using Japanese partly because it’s easy to do with code. Other languages have more exceptions.

def to_japanese(number)
    #
    # Protect our method from trying to work on numbers
    # it doesn't support
    #
    if not (0..99).include? number
        raise  "We can only translate numbers between 0 and 99" 
    end

    #
    # Keep it simple, use the variations of four and
    # nine that work in the tens column as well as
    # the ones column.  These are the numbers from
    # zero to nine.
    # 
    numbers = ['rei', 'ichi', 'ni', 'san', 'yon', 'go',
               'roku', 'nana', 'hachi', 'kyu']

    #
    # Modulus gives the remainder.
    # 12 divided by 10 is 1 with a remainder of 2.
    # It's a good way to get just the ones column.
    # Then we use regular division to get just the tens column
    #
    ones = number % 10
    tens = number / 10

    case tens
        # When we are doing 10 - 19
        when 1
            japanese = (0 == ones) ? 'ju' : 'ju ' + numbers[ones]

        # When we are doing 20 - 99
        when 2..9
            japanese = numbers[tens] + ' ju'

            if (ones > 0)
                japanese = [japanese, numbers[ones]].join(' ')
            end

        # Otherwise we are doing 0-9
        else
            japanese = numbers[ones]
    end

    return japanese
end

So there are a couple things I need to explain above. First is the case, when, else construct. The case statement tells Ruby that we are going to use the following expression (in this example the expression is a variable) with a bunch of comparisons. It’s a little nicer than doing a bunch of if/else statements. The first match is what gets run. Each case that we are checking is marked with the when statement. To translate it to English, it’s like saying “when tens is 1 do this”, “when tens is in the range 2..9 do that”, “otherwise do this”.

The next thing I have to explain is the (something) ? true : false construct. It’s a shorthand for an if/else statement. Essentially, we are saying that if the ones column is 0, just return ‘ju’ otherwise return ‘ju ’ plus the translation of the ones column. Have fun!

Comments

Leave a response

Comments