5. Functional Programming

Functional programming is a style of programming that

  • treats computation as the evaluation of mathematical functions, and

  • avoids changing-state and mutable data.

R has a lot of built in functions such as sapply, lapply, and tapply that encourages the functional style of programming. Additionally, there are helper libraries in R that makes functional programming easier, such as purrr. Take a look at the purrr documentation for the full application programming interface API.

Here, we cover the two most famous functions in functional programming:

  • map

  • reduce

5.1. Mapping

Mapping is used to transform a value into another one.

5.1.1. map

You may use the map function from the library purrr.

[1]:
library(purrr)

doubleIt <- function(x) 2 * x

a <- map(1:6, doubleIt)
print(paste(a, collapse=','))
[1] "2,4,6,8,10,12"

You may use the base lapply function.

[2]:
a <- lapply(1:6, doubleIt)
print(paste(a, collapse=','))
[1] "2,4,6,8,10,12"

Anonymous functions may also be supplied to lapply or map.

[3]:
a <- map(1:6, function(x) 3 * x)
print(paste(a, collapse=','))
[1] "3,6,9,12,15,18"

5.1.2. map2

The map2 function allows you to map over 2 lists.

[4]:
a <- map2(1:6, 1:6, function(x, y) x * y)
print(paste(a, collapse=','))
[1] "1,4,9,16,25,36"

5.1.3. pmap

The pmap function allows you to map over p lists.

[5]:
a <- pmap(list(c(2, 4, 6), c(3, 6, 9), c(4, 8, 12)), function(x, y, z) x + y + z)
print(paste(a, collapse=','))
[1] "9,18,27"

5.1.4. imap

The imap gives you a hook to the index of the element.

[6]:
a <- imap(10:15, function(i, x) paste(i, ':', x))
print(paste(a, collapse=' | '))
[1] "10 : 1 | 11 : 2 | 12 : 3 | 13 : 4 | 14 : 5 | 15 : 6"

5.1.5. modify

The function modify will modify elements in a matrix or data frame and return the same type.

[7]:
df <- data.frame(x=1:5, y=6:10)
print(df)
  x  y
1 1  6
2 2  7
3 3  8
4 4  9
5 5 10

Here, we use an anonymous function to transform the elements.

[8]:
df2 = modify(df, function(x) x * x)
print(df2)
   x   y
1  1  36
2  4  49
3  9  64
4 16  81
5 25 100

Here, we use a short-hand syntax to transform the elements.

[9]:
df2 = modify(df, ~ .x * .x)
print(df2)
   x   y
1  1  36
2  4  49
3  9  64
4 16  81
5 25 100

5.2. Reducing

Reducing collapses an array of elements into a scalar value.

5.2.1. reduce

[10]:
a <- reduce(1:5, function(a, b) a + b)
print(a)
[1] 15

You may also supply an initial value to reduce.

[11]:
a <- reduce(1:5, function(a, b) a + b, .init=10)
print(a)
[1] 25

5.2.2. accumulate

Accmulate is like reduce but returns the intermediate results as well.

[12]:
a <- accumulate(1:5, function(a, b) a + b)
print(a)
[1]  1  3  6 10 15

5.3. Filtering

Filter is removing elements from an array.

5.3.1. keep

The keep function retains elements that satisfies the predicate.

[13]:
a <- keep(1:10, ~ .x %% 2 == 0)
print(a)
[1]  2  4  6  8 10

5.3.2. discard

The discard function remove elements that satisifies the predicate.

[14]:
a <- discard(1:10, ~ .x %%2 != 0)
print(a)
[1]  2  4  6  8 10

5.4. Map, filter, reduce

Let’s put it all together in this simple example. We have a list of numbers that we will

  • multiply the elements by 3

  • filter the elements for even numbers

  • reduce the elements by adding

[15]:
a <- 1:10
b <- map(a, ~ .x * 3)
c <- keep(b, ~ .x %% 2 == 0)
d <- reduce(c, `+`, .init=0)

print(paste(c('[a]', a), collapse=','))
print(paste(c('[b]', b), collapse=','))
print(paste(c('[c]', c), collapse=','))
print(paste(c('d: ', d), collapse=''))
[1] "[a],1,2,3,4,5,6,7,8,9,10"
[1] "[b],3,6,9,12,15,18,21,24,27,30"
[1] "[c],6,12,18,24,30"
[1] "d: 90"