5 Object toss

5.1 Introduction

Let’s keep improving our function toss(), but now changing its output in order to return an object of class "toss".

5.2 Motivation for object "toss"

So far we have a constructor function coin() (from chapter 3) and a toss() (from chapter 4) which actually corresponds to the toss method for objects of class "coin":

Let’s use toss() to flip a coin 10 times:

Having obtained several tosses, we can calculate things like:

  1. the total number of tosses
  2. the total number of heads
  3. the total number of tails

In general, when tossing a coin, we are not only interested in keeping track of such tosses; we would also like to know (or keep track of) the number of tosses, the number of heads, and the number of tails. Consequently, it would be nice to have another class of object for this purpose.

How do you know that you need this new object class?

Well, this is precisely an example that illustrates the process of programming in general, and OOP in particular. This kind of decisions require some (or sometimes “a lot” of) thinking, and brainstorming time. To be honest, while I was writing this book and playing with "coin" objects and their tosses, I decided that it would be convenient to have an object of class "toss" containing the following information:

  • all the outcomes from the series of tosses
  • the total number of tosses
  • the total number of heads
  • the total number of tails

The most flexible type of data structure in R to store other data structures is a list. Having a vector of tosses, we can use a list to keep all the desired information:

The idea is to be able to invoke toss(), and then obtain an object like the list a in the above code. But do it in such a way that the output is an object of class "toss".

5.2.1 Auxiliary Constructor

For convenience purposes, we can write an auxiliary constructor function, which I will call make_toss(). This function will take an input vector (i.e. a character vector with "heads" and "tails" elements), and it will return an object of class "toss":

This auxiliary function is not intended to be called by the user. Instead, it’s an internal function for auxiliary purposes.

5.3 Main Function toss()

Now that we have the auxiliary function make_toss(), we can integrate it inside the specific method toss.coin(). In this way, the function toss.coin() becomes the master function: the one designed to be called by the user:

This is how toss() works:

You may ask: “Why do we need a function make_toss(), and another function toss()?”. Can’t we just write a single function suppertoss() that does everything at once?:

The short answer is: yes, you can. And probably this is what most beginners tend to do. The reason why I decided to break things down into simpler and smaller functions is because I went already through a couple of implementations, and realized that it was better to have the auxiliary function make_toss(). Also, it is good practice to write short functions that preferably do one thing.

Here’s a brief recap of the main functions we have so far:

  • coin() is a constructor function to create objects of class "coin".
  • toss() is a generic "toss" method.
  • make_toss() is an auxiliary function that takes a "coin" and a vector of flips, and which produces an object "toss".
  • toss.coin() is the specific "toss" method to be used on "coin" objects.
  • notice that make_toss() is called by toss.coin().

5.4 Upgrading toss()

Let’s consider our quarter coin, and apply toss() on it:

toss() is working as expected, and you can try it with different values for times. The only issue is that a distracted user could pass an unexpected value for the argument times:

R produces an error when times = -4, but it’s an error that may not be very helpful for the user. The error message clearly says that 'size' is an invalid argument, but toss() just has one argument: times.

To be more user friendly, among other reasons, it would be better to check whether times has a valid value. One way to do that is to include a conditional statement like the following one:

Once again, it is good practice to write short functions that preferably do one thing. In this case, we could define a checking function check_times() to make sure that times has a valid value:

Once check_times() has been defined, we can include it inside toss():

5.4.1 In Summary

The more you understand a problem (i.e. phenomenon, process), the better you will be prepared to design objects, and program their corresponding methods, auxiliary functions, classes, etc.

In my experience, you will very likely need to iterate several times with the creation of objects and functions for your code. At one iteration you will realize that you need to break down a given function into two ore more simpler functions. Sometimes you will see an opportunity to create a secondary function to check ceratin inputs. Likewise, you will identify situations when derived methods are needed to make your code more flexible and user friendly. Of course, like anything in this life, learning when all these components are needed takes a lot of time and practice.


Make a donation

If you find this resource useful, please consider making a one-time donation in any amount. Your support really matters.