Functions - defining and using in programs

As our skills grow, our programs become larger. By and by managing long sheet of code becomes difficult. Is it possible to conveniently split the program into smaller parts?

General approach is to put certain fragments of code into functions, which could be defined separately. In other languages they also could be called procedures or subroutines. Let's see some examples.

Hypotenuse function

Despite the weird name we know from school, this calculation is extremely useful in graphic applications. Suppose there are several points of interest on the screen (perhaps, rounded buttons etc) and user clicks somewhere - and we want to know which point is "hit" (if any). Let user's click have coordinates X, Y and button's center be at Xc, Yc - in this case the distance between two points is calculated according to Pythagoras:

dX = X - Xc                      -- difference by X
dY = Y - Yc                      -- difference by Y
dist = math.sqrt(dX^2 + dY^2)    -- pythagorean formula

We can combine it into single string, but anyway repeating such equation in several place, if necessary, will make our program look messy. Some languages have ready hypot(dx, dy) function, but not Lua. No problem, let's define it:

function hypot(dx, dy)
    return math.sqrt(dx^2 + dy^2)
end

dist = hypot(X-Xc, Y-Yc)

The last line shows how we use (or "call") the defined function: just its name and arguments in parentheses. Arguments in the function definition (dx and dy) are then used as variables visible only inside the function - to which actual values (passed upon the call) are assigned. The word return is used to stop executing the function and "return" the calculated value into the outer code, from where the function was called.

The cool bonus feature of Lua is that we can arrange this new function to live along the other math functions:

math.hypot = function(dx, dy)
    return math.sqrt(dx^2 + dy^2)
end

dist = math.hypot(X-Xc, Y-Yc)

In this case the function is "nameless" but it is immediately assigned to the math.hypot variable so it is used the same way as if such were its name!

Cockroach drawing

Suppose we want to create a game with graphical element representing cockroaches or other bug. We start with designing the shape of our "protagonist":

graph.init()
graph.rect(0, 0, graph.w, graph.h, 'white')

graph.circle(100, 100, 15)      -- body
graph.circle(100, 76, 9)        -- head
graph.line(74, 100, 85, 100)    -- legs follow
graph.line(115, 100, 126, 100)
graph.line(78, 85, 88, 93)
graph.line(122, 85, 112, 93)
graph.line(78, 115, 88, 107)
graph.line(122, 115, 112, 107)

It may take us some minutes to figure out suitable coordinates by trial and error. But now, when the first cockroach is ready, how can we draw another, in different coordinates? We shouldn't recalculate this all manually for different starting point insteand of 100, 100 - should we???

Of course no. Let's wrap it in a function:

graph.init()
graph.rect(0, 0, graph.w, graph.h, 'white')

function cockroach(x, y, clr)
    graph.color(clr)
    graph.circle(x, y, 15)
    graph.circle(x, y-24, 9)
    graph.line(x-26, y, x-15, y)
    graph.line(x+15, y, x+26, y)
    graph.line(x-22, y-15, x-12, y-7)
    graph.line(x+22, y-15, x+12, y-7)
    graph.line(x-22, y+15, x-12, y+7)
    graph.line(x+22, y+15, x+12, y+7)
end

cockroach(100, 300, 'red')
cockroach(300, 100, 'green')
cockroach(500, 300, 'blue')

We added third parameter so we define not only coordinates, but also colors. As for coordinates, you can see we subtracted 100 from our initial "trial" findings everywhere and replaced it with x or y correspondingly.

Exercise: RGB

You may remember in the previous lesson we complained that preparing strings defining colors in RGB form is tedious due to necessity of concatenating string like rgb(115,200,90) using three values and string pieces between them (so, 7 in total).

Now, create a function rgb which takes three arguments and produces such a string. Of course inside it still be concatenation, but it is in only one place. Copy the example with gradient filling and modify it so your function is called instead of concatenations.