Title: | Functional Programming |
---|---|
Description: | High-order functions for data manipulation : sort or group data, given one or more auxiliary functions. Functions are inspired by other pure functional programming languages ('Haskell' mainly). The package also provides built-in function operators for creating compact anonymous functions, as well as the possibility to use the 'purrr' package syntax. |
Authors: | Pierre-Yves Berrard [aut, cre] |
Maintainer: | Pierre-Yves Berrard <[email protected]> |
License: | GPL-2 |
Version: | 0.3.0 |
Built: | 2025-03-06 03:32:17 UTC |
Source: | https://gitlab.com/py_b/funprog |
The funprog package implements in R some functions existing in other pure functional programming languages.
The package provides high-order functions, for example :
Helper functions can be used in conjunction with the main functions :
%on%
combines two functions into one and serves to create a
predicate function to group_if
descending
is used to reverse the output of a sorting
function used with sort_by
purrr
syntaxIf the purrr package is installed, you
can use its special syntax to create very compact anonymous functions, for
example ~ abs(.x - .y) > 1
instead of function(x, y) abs(x - y)
> 1
.
Execute the binary function f on the results of applying unary function g to two arguments x and y.
f %on% g
f %on% g
f |
a binary function. |
g |
a unary function. |
Formally, %on%
is defined this way :
function(f, g) function(x, y) f(g(x), g(y))
.
f can be a function taking two arguments but also a variadic function (i.e.
whose first argument is ...
), which will be fed with exactly two
arguments.
A typical usage of this function is in combination with function like
group_if
.
A binary function. This function transforms 2 inputs (with g) and combines the outputs (with f).
h <- max %on% abs h(-2, 1)
h <- max %on% abs h(-2, 1)
Transform a function (typically used in sort_by
), so that its
ouput can be sorted in descending order.
descending(f)
descending(f)
f |
a function to modify. |
A function returning a numeric vector which, if passed to
order
, will be used to sort some data.
desc_abs <- descending(abs) x <- -2:1 order(abs(x)) order(desc_abs(x))
desc_abs <- descending(abs) x <- -2:1 order(abs(x)) order(desc_abs(x))
Split a vector or a list into groups, given a predicate function.
group_if(x, predicate, na.rm = FALSE) group_eq(x, na.rm = FALSE)
group_if(x, predicate, na.rm = FALSE) group_eq(x, na.rm = FALSE)
x |
a vector or a list to split into groups. |
predicate |
a binary function returning a boolean value. |
na.rm |
if x is atomic, delete missing values before grouping. |
predicate
will be applied to 2 adjacent elements. If it evaluates to
TRUE
, those elements belong to the same group, otherwise they belong
to different groups.
Grouping on equality is the most natural approach, therefore group_eq
is a convenient shortcut defined as
group_if(x, predicate = `==`)
for an atomic vector;
group_if(x, predicate = identical)
for a list.
group_if
(resp. group_eq
) is inspired by groupBy
(resp.
group
) in Haskell.
Note that group_if
behaves a little differently : while in
Haskell, the comparison is made with the first element in the group, in this
R-version the comparison is made with the adjacent element.
The operator %on% may be helpful to create a predicate with readable syntax.
A list where each element is a group (flattening this list should give back the same values in the same order). Element names are kept.
x1 <- c(3, 4, 2, 2, 1, 1, 1, 3) group_eq(x1) group_if(x1, `<=`) group_if(x1, function(x, y) abs(x - y) > 1) x2 <- c(3, 4, 2, -2, -1, 1, 1, 3) group_if(x2, `==` %on% abs) x3 <- list(1:3, 1:3, 3:5, 1, 2) group_if(x3, `==` %on% length)
x1 <- c(3, 4, 2, 2, 1, 1, 1, 3) group_eq(x1) group_if(x1, `<=`) group_if(x1, function(x, y) abs(x - y) > 1) x2 <- c(3, 4, 2, -2, -1, 1, 1, 3) group_if(x2, `==` %on% abs) x3 <- list(1:3, 1:3, 3:5, 1, 2) group_if(x3, `==` %on% length)
Apply a function to a value, then reapply the same function to the result and so on... until a condition on the result is met (or a certain number of iterations reached).
iterate(x, f, stop_fun = NULL, stop_n = Inf, accumulate = FALSE)
iterate(x, f, stop_fun = NULL, stop_n = Inf, accumulate = FALSE)
x |
initial value. |
f |
the function to apply. |
stop_fun |
a predicate (function) evaluated on the current result, which
will stop the process if its result is |
stop_n |
maximal number of times the function will be applied (mandatory
if |
accumulate |
by default, the function returns only the last element. To
get the list of all intermediate results, turn this parameter to
|
As it is a very generic function (x
can be any type of object) and the
number of computations cannot be known in advance, iterate
can be
quite inefficient (particularly if you use accumulate = TRUE
).
The last result, or the list of all results if
accumulate = TRUE
.
# https://en.wikipedia.org/wiki/Collatz_conjecture syracuse <- function(x) if (x %% 2) 3 * x + 1 else x / 2 iterate( 10, syracuse, stop_fun = function(n) n == 1, accumulate = TRUE ) # https://en.wikipedia.org/wiki/H%C3%A9non_map henon_attractor <- iterate( c(-1, 0.1), function(x) c(1 - 1.4 * x[1]^2 + x[2], 0.3 * x[1]), stop_n = 5000, accumulate = TRUE ) plot( sapply(henon_attractor, function(.) .[1]), sapply(henon_attractor, function(.) .[2]), pch = "." )
# https://en.wikipedia.org/wiki/Collatz_conjecture syracuse <- function(x) if (x %% 2) 3 * x + 1 else x / 2 iterate( 10, syracuse, stop_fun = function(n) n == 1, accumulate = TRUE ) # https://en.wikipedia.org/wiki/H%C3%A9non_map henon_attractor <- iterate( c(-1, 0.1), function(x) c(1 - 1.4 * x[1]^2 + x[2], 0.3 * x[1]), stop_n = 5000, accumulate = TRUE ) plot( sapply(henon_attractor, function(.) .[1]), sapply(henon_attractor, function(.) .[2]), pch = "." )
Split a vector or a list in 2 groups, given a predicate function.
partition(x, predicate)
partition(x, predicate)
x |
vector or list to partition. |
predicate |
a function returning a boolean value, to apply to each element of x. |
A list of two elements. The first element contains elements of x satisfying the predicate, the second the rest of x. Missing values will be discarded.
partition(c(2, 1, 3, 4, 1, 5), function(x) x < 3) partition(list(1:3, NA, c(1, NA, 3)), anyNA)
partition(c(2, 1, 3, 4, 1, 5), function(x) x < 3) partition(list(1:3, NA, c(1, NA, 3)), anyNA)
Sort a vector or a list, given one or more auxiliary functions.
sort_by(x, ..., method = c("auto", "shell", "radix"))
sort_by(x, ..., method = c("auto", "shell", "radix"))
x |
vector or list to sort. |
... |
one or several functions to apply to |
method |
the method for ties (see |
The output of the first function will be used as first key for sorting, the output of the second function as second key, and so on... Therefore, these outputs should be sortable (i.e. atomic vectors).
sort_by
is inspired by sortBy
in Haskell.
A vector or list containing rearranged elements of x
.
order
which is used for rearranging elements.
sort_by(-3:2, abs) sort_by(-3:2, abs, function(x) -x) sort_by(list(5:7, 0, 1:4), length) sort_by(list(1:2, 3:4, 5), length, descending(sum))
sort_by(-3:2, abs) sort_by(-3:2, abs, function(x) -x) sort_by(list(5:7, 0, 1:4), length) sort_by(list(1:2, 3:4, 5), length, descending(sum))
Remove duplicate elements, given a transformation.
unique_by(x, f, first = TRUE)
unique_by(x, f, first = TRUE)
x |
a vector or a list. |
f |
a function to apply to each element of |
first |
if several elements are identical after being transformed by
|
An object of the same type as x. Only elements that are unique after
being transformed by f
are kept.
unique_by(-3:2, abs) unique_by(-3:2, abs, first = FALSE) unique_by(c(1, 2, 4, 5, 6), function(x) x %% 3) unique_by(list(1:2, 2:3, 2:4), length)
unique_by(-3:2, abs) unique_by(-3:2, abs, first = FALSE) unique_by(c(1, 2, 4, 5, 6), function(x) x %% 3) unique_by(list(1:2, 2:3, 2:4), length)