# initpy/go-book

Fetching contributors…
Cannot retrieve contributors at this time
626 lines (500 sloc) 20.7 KB

# Getting funky with functions

In the previous chapters, we went from how to declare variables, pointers, how to write basic control structures, how to combine these control structures to write functions. Then we went back to data, and how to combine basic data types to create composite data types.

And now, armed with this knowledge, we're ready to work on advanced aspects of functions.

I decided to divide this task in two chapters, not because the things we're about to study are hard, but mainly because it's easier, and more fun to work on a few topics at a time.

Do you remember when I made you :ref:`cringe <Older100>` at the idea of writing a function `Older100` that will not accept less than 100 `person`s? `struct`s as its input?

Well I lied a little bit, but it was a good lie. I duct-taped you to a chair with that blindfold on with the best of intentions, I swear! It was an excellent reason to learn about arrays, and later about slices! Relax... it's for your own safety! ;)

The truth is: you can have functions that accept a variable number of input parameters of the same type. They're called variadic functions.

These functions can be declared like this:

`func function_name(args ...inputType) (output1 OutputType1 [, output2 OutputType2 [, ...]])`

There's no difference from the way we learned to declare functions, except the three dots (called an ellipses) thing:`...inputType` which says to the function that it can recieve a variable number of parameters all of them of type `inputType`.

How does this work? Easy: In fact, the `args` identifier you see in the input parameter is actually a slice which contains all of the parameters passed in ( which should all be of type `inputType`).

A slice! Yay! So we can, in the function's body, iterate using `range` to get all the parameters! Yes, that's the way you do it. You play the guitar on the mtv... er, sorry. I got a case of Mark Knopfler fever.

Let's see an example:

Output:

The older of Paul and Jim is: Jim
The older of Paul, Jim and Sam is: Sam
The older of Paul, Jim, Sam and Rob is: Sam
When Karl is alone in a group, the older is: Karl
In an empty group there is no older person

Look how we called the function `Older` with 2, 3, 4, 1 and even no input parameters at all. We could have called it with 100 `person`s if we wanted to.

Variadic functions are easy to write and can be handy in a lot of situations. By the way, I promised to tell you about the built-in function called `append` that makes appending to slices easier! Now, I can tell you more, because, guess what? The built-in `append` function is a variadic function!

### The built-in append function

The append function has the following signature:

`func append(slice []T, elements...T) []T`.

It takes a slice of type `[]T`, and as many other `elements` of the same type `T` and returns a new slice of type `[]T` which includes the given elements.

Example:

Output:

At first:
slice = [1 2 3]
len(slice) = 3
Let's append 4 to it
slice = [1 2 3 4]
len(slice) = 4
Let's append 5 and 6 to it
slice = [1 2 3 4 5 6]
len(slice) = 6
Let's append 7, 8, and 9 to it
slice = [1 2 3 4 5 6 7 8 9]
len(slice) = 9

You can even give a variadic function a slice of type `[]T` instead of a list of comma-separated parameters of type `T`.

Example:

Again, just in case you glossed over the comment, notice the ellipses '`...`' immediately following the second slice argument to the variadic function.

### Using the append function to delete an element

Suppose that we have a slice `s` and that we would like to delete an element from it. There are 3 cases to consider:

• The element we'd like to delete is the first one of the slice.
• The element we'd like to delete is the last one of the slice
• The element we'd like to delete is the one at index `i` of the slice (where `i` is between the first and the last ones)

Let's write a function that deletes the element at a given index `i` in a given slice of `int`s and see how the `append` function can help.

Output:

In the beginning...
slice = [1 2 3 4 5 6 7 8 9 10]
Let's delete the first element
slice = [2 3 4 5 6 7 8 9 10]
Let's delete the last element
slice = [2 3 4 5 6 7 8 9]
Let's delete the 3rd element
slice = [2 3 5 6 7 8 9]

The lines 6 and 7 of our program are fairly easy to understand. Aren't they? We re-slice our slice omitting the first and the last elements respectively.

Now, the case where the element is not the first nor the last one. We assign to our slice the result of `append` of the slice starting from the first up to, but not including, the `i` th element and the slice that starts from the element after the `i` th (i.e. i+1) up to the last one. That is, we made our slice contain elements from the right sub-slice and the left one to the element that we want to delete.

Simple, isn't it? Now, we actually complicated the `delete` function in vain. We could have written it like this:

Yes, it will work. Go and try it. Can you figure out why? (Hint: empty. Hint Hint: empty.)

## Recursive functions

Some algorithmic problems can be thought of in a beautiful way using recursion!

A function is said to be recursive when it calls itself within it's own body. For example: the Max value in a slice is the maximum of the Max value of two sub-slices of the same slice, one starting from 0 to the middle and the other starting from the middle to the end of the slice. To find out the Max in each sub-slice we use the same function, since a sub-slice is itself a slice!

Enough talk already, let's see this wonderous beast in action!

Output:

Max(s) = 8

Another example: How to invert the data in a given slice of ints? For example, given `s := []int {1, 2, 3, 4, 5}`, we want our program to modify `s` to be `{5, 4, 3, 2, 1}`.

A recursive strategy to do this involves first swapping the first and the last elements of `s` and then inverting the slice between these elements, i.e. the one from the 2nd element to the one just before the last one.

Let's write it:

Output:

slice = [1 2 3 4 5]
Invert(slice) = [5 4 2 3 1]

Exercise Recursive absurdity! The SUM of the ints in a given slice is equal to the first element plus the SUM of the subslice starting from the second element. Go and write this program.

And these are but a few examples, many problems can be solved using recursion. You know, just like walking: to walk, you put one foot in front of the other and you walk :)

## The defer statement

Sometimes you'd like to do something just before a `return` statement of a function, like for example closing an opened file. Now, suppose that our function has many spots where we call `return`, like for example in a `if`/`else` chain, or in a `switch` statement. In these cases we'd do that something as many times as there is a `return`.

The `defer` statement fixes this annoying irreverant mind-numbing repetition.

The `defer` statement schedules a function call (the deferred function) to be run immediately before the function executing the `defer` returns.

Let's see an example to make the idea clearer:

The os package comes with functions to open, close, create... files, Go figure!. Being a good lil programmer means that we have to close every file we open in our program after we've done our nefarious business with it's contents.

Now, imagine a function that opens a file, processes the data in it, but has many return spots in its body. Without `defer` you'll have to manually close any already opened file, just before the `return`.

Look at this simple program from Effective Go.

Our function `Contents` needs a file name as its input, and it ouputs this file's content, and an eventual error (for example in case the file was not found).

On line 10, we open the file. If the returned error from `os.Open` is not `nil` then we were unable to open the file. So we return an empty string and the same error that `os.Open` returned.

Then on line 14, we `defer` the call to `f.Close()` so that the file will be automatically closed when the function emits a return (2 spots in our function).

Then we declare a `result` slice that will contain all the bytes read from the file. And we make a buffer `buf` of 100 bytes that will be used to read 100 bytes at a time from our file using the call to `f.Read`. This buffer will be appended to``result`` each time it's filled with `f.Read`.

And we loop. If we get an error while reading, we check if its `os.EOF` (EOF means End Of File) if so, we `break` from the loop, else it's another error so we return an empty string and that error.

At the end, we return the slice `result` converted to a string, using a type casting and `nil` which means that there was no error while retrieving the contents of the file.

I know, this example may look a little bit hard, especially since we have never used the `os` package before, but the main goal of it is to show you a use of the `defer` statement. And how it can be used to guarantee two things:

• Using `defer f.Close()` in the beginning makes sure that you will never forget to close the file, no matter how the function might change in the future, and no matter how many `return` spots will be added/removed from it.
• The close sits near the open, which is much clearer than placing it at the end of the function.

### Multiple defer calls

You can defer as many function calls as you want, and they'll be executed before the containing function returns in a LIFO (Last In First Out) order. That is, the last one to be deferred will be run first, and the first one will be executed at last.

Example:

Output:

Running function B
Running function A

And that's it for this chapter. We learned how to write variadic, and recursive functions, and how to defer function calls. The next chapter will be even more interesting, we will see how functions are, actually, values!

Something went wrong with that request. Please try again.