User Defined Functions in R Part II

In this tutorial, you will learn about what is recursive function in R, how to write a recursive function, what is nested function, how to use the ... argument to the function. You will also learn about the scoping rules for functions in R, local and global variables in R.

Recursive function in R

Recursive function is a function that call itself. The recursive function call itself again and again to get the final result.

The best example of recursive function is the factorial of a number. The factorial of positive number $n$ is defined as

$$ \begin{aligned} n! &=n\cdot (n-1)\cdot (n-2) \cdots 3\cdot 2 \cdot 1\\ & = n\cdot (n-1)!\\ & = n\cdot (n-1) \cdot (n-2)! \end{aligned} $$

That is to compute $n!$ we need to compute $(n-1)!$. Again to compute $(n-1)!$, we need to compute $(n-2)!$ and so on. Such a function is called a recursive function.

Note that there is a built-in factorial(x) function available in R. The example above is just to show you how to define a recursive function in R and how it works.

Example 1: Factorial using a recursive function

Factorial <- function(x) {
  if (x == 0) {
    fact <- 1
    return(1)
  } else{
    fact <- x * Factorial(x - 1)
  }
  return(fact)
}
Factorial(5)
[1] 120

Note that in the above example to calculate 5!, we use Factorial(5) function. But when we call the Factorial(5) function, the function calls Factorial(4). The process is repeated till the value of x reaches 1.

Nested function in R

When we use one function inside the another function then such function is called nested function.

Example 2: Nested function in R

Let us consider an example of nested function to compute power of a number.

# main function
Power <- function(n) {
  # sub function
  pow <- function(x) {
    x ^ n
  }
  pow
}

In the above R code, there are two function Power() and pow(). The first function Power() is the main function or outer function and second function pow() is the sub function or inner function.

If you execute the function Power() with argument, say, 2, it will not calculate anything but display the environment of the inner function.

Power(2)
function(x) {
    x ^ n
  }
<environment: 0x0000000008b9d6e8>
# Assign the function Power(2) to square
square <- Power(2)

Above R code creates a child function square() using Power() function. Now we can execute the square() function with some argument.

# compute the square of 3
square(3)
[1] 9

Let us create another child function cube() using Power() function to compute the cube of a number.

cube<-Power(3)
cube(3)
[1] 27

The parent function power() creates two child functions square() and cube().

When we assign the Power() function to some other object, it will create a child function. Once the child function is created we can use the child function to calculate the value of the function.

Dots ... argument to a function

The dots (...) argument allows us to cascade arguments to other functions without including them in the function definition. The dots (...) allows us to pass arguments to other functions inside the function. While defining a function if the number of arguments passed to the function cannot be known in advance, we can use the dots (...) argument.

Example 3: dots arguments in function

If the input argument to mean() function contains NA values, then we have to specify the argument na.rm=TRUE to get the result.

Let us define a user-defined function MyMean() using ... argument as follows:

# user-defined function with ... argument
MyMean <- function(x, ...) {
  return(mean(x, ...))
}
# create x vector
x <- c(NA, 1:10, NA)
# calculate mean of x using MyMean function
MyMean(x)
[1] NA

In the above R code, as the vector x contains NA values the function MyMean() returns the result NA.

If we pass ... argument as na.rm=TRUE to the MyMean() function, it will calculate the mean of x by removing NA values.

MyMean(x, na.rm = TRUE)
[1] 5.5

Example 4: dots arguments in function

Let us define user-defined function to plot a graph based on built-in plot() function with dots (...) argument as follows:

myplot <- function(x, y, ...) {
  plot(x, y, col = "blue", ...)
}

In the above myplot() function, we have used two arguments x,y and third argument as ....

In the plot() function there are various arguments

# create x vector as a sequence
x <- seq(1, 5, 0.1)
# create y vector
y <- exp(-x)

Use myplot() function with argument x and y. Along with these arguments we can use additional arguments of plot() function like main for the main title of the graph and ylab to give label to y axis.

myplot(x,y,main="y=e^(-x)",ylab=expression(e^(-x)))

Suppose we want the type of plot as a line with line width 2, we can use an additional argument as type="l" and lwd=3.

myplot(x,y,type="l",lwd=3,
       main="Graph of exponential function")

Suppose you want to define a new function based on the existing function. Then the ... argument is used when you don't want to copy the all argument list of the original function.

Global and Local Variables in functions

The variables outside the function are the global variables and the variables inside the function are called the local variables to that function.

Example 5: Global and local variables in functions

a <- 25   # global variable
myfun <- function(x) {
  p <- 2.5  # local variable
  new_fun <- function() {
    # sub function
    p * (x + a)  # a is a free variable
  }
  return(new_fun())
}
myfun(2)
[1] 67.5

In the above R code, there are two functions one is myfun() and other is new_fun(). The myfun() is the outer function and new_fun() is the inner function.

The variable a which is outside of the main function is the global variable. The variable p in myfun() is the local variable to the function. For the new_fun() the variable a is a free variable.

Scoping Rules for variables in the function

Rules for working out which variable to use in which environment are called scoping rules.

  • First look for a variable inside the function.
  • Then look for a variable inside the function that the function was defined in (if any).
  • If it doesn't find then it will look in the parent environment.
  • Finally look in the global environment.

Example 6: Scoping rules

outer <- function(d1) {
  d2 <- 10  # local variable
  inner <- function(d1) {
    # sub function
    (d1 + d2) / 5  # inner function have an access to d2
  }
  inner(d1)
}
outer(5)
[1] 3

In the above R code, the function inner() is a sub-function of outer() function. So the inner() function have access to y from the outer() function environment.

outer1 <- function(d1) {
  # d2 is a local variable to outer1
  d2 <- 10  
  inner1(d1)
}

inner1 <- function(d1) {
  (d1 + d2) / 5  
# inner1 function doesn't have an access to d2
}

outer1(5)
## Error in inner1(d1) : object 'd2' not found

In the above R code, the inner1() function is a separate function but we call it within the outer1() function. So even though the y variable is defined in outer1() function, the inner1() function doesn't have access to the variable y. In such a situation, R display an error message.

Endnote

In this tutorial, you learned about how to create recursive functions in R, how to define nested function in R and how to use ... argument to the user-defined function.

To learn more about functions in R, please refer to the following tutorials:

Hopefully you enjoyed this tutorial on user-defined functions in R part II.

VRCBuzz co-founder and passionate about making every day the greatest day of life. Raju is nerd at heart with a background in Statistics. Raju looks after overseeing day to day operations as well as focusing on strategic planning and growth of VRCBuzz products and services. Raju has more than 25 years of experience in Teaching fields. He gain energy by helping people to reach their goal and motivate to align to their passion. Raju holds a Ph.D. degree in Statistics. Raju loves to spend his leisure time on reading and implementing AI and machine learning concepts using statistical models.

Leave a Comment