2.4 Desarrollo de funciones y paquetes

Antes de ponerse a programar, sobre todo si puede terminar siendo un código complejo, la recomendación es hacer una búsqueda por si resulta que ya está implementado (o hay algo que podemos tomar como base; es lo bueno de GNU!): en la descripción de los paquetes en CRAN, en los buscadores especializados (rdrr.io, RDocumentation o RSeek), en foros de programación (StackOverflow, StackOverflow.es, Cross Validated), en listas de correo (r-project.org, r-help-es) o directamente en Google (añadiendo “r-project” o similar en la búsqueda).

El primer paso es escribir el código como si fuese un programa, asignando valores de prueba a los parámetros, y cuando nos aseguramos de que funciona, reescribirlo como función (yo suelo mantener unos valores de prueba como comentarios por si quiero ejecutar paso a paso el cuerpo de la función).

Al finalizar, la recomendación es documentar la función, preferiblemente empleando el formato roxygen2 (ver el menú de RStudio Help > Roxygen Quick Reference). Por ejemplo:

# read_excel_list(path, pattern, ...) 
# ·············································
#' Lee los ficheros xls y xlsx de un directorio
#' 
#' @param path Ruta al directorio con los ficheros excel 
#' (por defecto el directorio de trabajo).
#' @param pattern Expresión regular empleada en la selección de ficheros 
#' (ver `list.files()`).
#' @param ... Parámetros adicionales de `readxl::read_excel()`.
#' @return Una lista cuyas componentes son las correspondientes tablas de datos 
#' (`tibble`) y con nombres los nombres de los archivos sin extensión.
#' @examples \dontrun{
#' data_list <- read_excel_list("datos") # "./datos"
#' data_all <- dplyr::bind_rows(data_list)
#' }
# ·············································
# Pruebas: 
#   readxl::readxl_example("geometry.xls")
#   path = "C:/Program Files/R/R-4.2.2/library/readxl/extdata"
#   pattern = "\\.(xls|xlsx)$"
# Pendiente:
#   - Controlar posible error al leer
# ·············································
read_excel_list <- function(path = ".", pattern = "\\.(xls|xlsx)$", ...) {
  if (!requireNamespace(readxl))  stop("'readxl' package required")
  files <- dir(path, pattern = pattern, full.names = TRUE) # ?list.files
  data_list <- vector(length(files), mode = 'list')
  for (i in seq_along(files)) 
      data_list[[i]] <- readxl::read_excel(files[i], ...)
  data_names <- sub('\\.xlsx$', '', basename(files)) 
  names(data_list) <- data_names
  data_list
}

Como ya se comentó, en ocasiones se emplea como punto de partida una función ya implementada en algún paquete de R. En RStudio la forma más sencilla de obtener el código de la función es emplear View(funcion) (si la función es visible, en caso contrario View(paquete:::funcion)). Si la función llama a funciones internas (que no se exportan en el namespace) del paquete que la implementa, podríamos emplear también los tres dobles puntos para llamarlas, pero la recomendación sería descargar el código del paquete (si está en CRAN, un fichero comprimido de la forma paquete_x.y.z.tar.gz que se puede descargar en la sección Downloads de la web del paquete https://CRAN.R-project.org/package=paquete).

La mejor forma de organizar funciones es crear un paquete. Para ello se recomienda seguir:

Wickham, Hadley (2015). R packages: organize, test, document, and share your code (actualmente 2ª edición en desarrollo con H. Bryan), O’Reilly, 1ª edición.

También puede ser de utilidad el manual Writing R Extensions para información adicional.