r

Obtenga todas las funciones de origen



(5)

Creo que esta expresión regular captura casi todos los tipos válidos de funciones (operador binario, funciones de asignación) y todos los caracteres válidos en el nombre de una función, pero es posible que haya perdido un caso límite.

# lines <- readLines("functions.R") lines <- c( "`%in%` <- function", "foo <- function", "foo2bar <- function", "`%in%`<-function", "foo<-function", ".foo <-function", "foo2bar<-function", "`foo2bar<-`<-function", "`foo3bar<-`=function", "`foo4bar<-` = function", "` d d` <- function", "lapply(x, function)" ) grep("^`?%?[.a-zA-Z][._a-zA-Z0-9 ]+%?(<-`)?`?//s*(<-|=)//s*function", lines) #> [1] 1 2 3 4 5 6 7 8 9 10 funs <- grep("^`?%?[.a-zA-Z][._a-zA-Z0-9 ]+%?(<-`)?`?//s*(<-|=)//s*function", lines, value = TRUE) gsub("^(`?%?[.a-zA-Z][._a-zA-Z0-9 ]+%?(<-`)?`?).*", "//1", funs) #> [1] "`%in%`" "foo " "foo2bar " "`%in%`" "foo" #> [6] ".foo " "foo2bar" "`foo2bar<-`" "`foo3bar<-`" "`foo4bar<-`"

En R, estoy usando source() para cargar algunas funciones:

source("functions.R")

¿Es posible obtener la lista de todas las funciones definidas en este archivo? Como nombres de funciones. (¿Quizás source() sí mismo puede de alguna manera devolverlo?).

PD: El último recurso sería llamar a source() segunda vez como local({ source(); }) y luego hacer ls() dentro y filtrar funciones, pero eso es demasiado complicado: ¿hay una solución más fácil y menos torpe?


Creo que la mejor manera sería obtener el archivo en un entorno temporal. Consulte ese entorno para todas las funciones, luego copie esos valores en el entorno principal.

my_source <- function(..., local=NULL) { tmp <- new.env(parent=parent.frame()) source(..., local = tmp) funs <- names(tmp)[unlist(eapply(tmp, is.function))] for(x in names(tmp)) { assign(x, tmp[[x]], envir = parent.frame()) } list(functions=funs) } my_source("script.R")


Es un poco torpe, pero podría ver los cambios en los objetos antes y después de la llamada de source esta manera.

# optionally delete all variables #rm(list=ls()) before <- ls() cat("f1 <- function(){}/nf2 <- function(){}/n", file = ''define_function.R'') # defines these #f1 <- function() {} #f2 <- function() {} source(''define_function.R'') after <- ls() changed <- setdiff(after, before) changed_objects <- mget(changed, inherits = T) changed_function <- do.call(rbind, lapply(changed_objects, is.function)) new_functions <- changed[changed_function] new_functions # [1] "f1" "f2"


Esto adapta el código utilizado en la publicación de mi comentario para buscar una secuencia de tokens (símbolo, operador de asignación, luego función), y debe tomar cualquier función definida. No estoy seguro de si es robusto como la respuesta de MrFlick, pero es otra opción:

source2 <- function(file, ...) { source(file, ...) t_t <- subset(getParseData(parse(file)), terminal == TRUE) subset(t_t, token == "SYMBOL" & grepl("ASSIGN", c(tail(token, -1), NA), fixed = TRUE) & c(tail(token, -2), NA, NA) == "FUNCTION")[["text"]] }


Si este es su propio script para que tenga control sobre cómo está formateado, una simple convención sería suficiente. Solo asegúrese de que cada nombre de función comience en el primer carácter de su línea y que la function palabra también aparezca en esa línea. Cualquier otro uso de la function palabra debe aparecer en una línea que comienza con un espacio o tabulación. Entonces una solución de una línea es:

sub(" .*", "", grep("^//S.*function", readLines("myscript.R"), value = TRUE))

Las ventajas de este enfoque son que

  • Es muy simple . Las reglas simplemente se establecen y solo se necesita una línea simple de código R para extraer los nombres de las funciones. Regex también es simple y para un archivo existente es muy fácil de verificar: simplemente utilice la function palabra y verifique si cada aparición que se muestra sigue la regla.

  • No es necesario ejecutar la fuente. Es completamente estático .

  • en muchos casos no necesitará cambiar el archivo fuente en absoluto y en otros habrá cambios mínimos. Si está escribiendo el guión desde cero con esto en mente, es aún más fácil de organizar.

Hay muchas otras alternativas a lo largo de la idea de convenciones. podría tener una expresión regular más sofisticada o podría agregar # FUNCTION al final de la primera línea de cualquier definición de función si está escribiendo el script desde cero y luego extrae esa frase y extrae la primera palabra en la línea, pero la sugerencia principal Aquí parece particularmente atractivo debido a su simplicidad y las otras ventajas enumeradas.

Prueba

# generate test file cat("f <- function(x) x/nf(23)/n", file = "myscript.R") sub(" .*", "", grep("^//S.*function", readLines("myscript.R"), value = TRUE)) ## [1] "f"