Skip to content
Snippets Groups Projects
cache_name.R 4.05 KiB
Newer Older
  • Learn to ignore specific revisions
  • pbac's avatar
    pbac committed
    # Do this in a separate file to see the generated help:
    #library(devtools)
    #document()
    #load_all(as.package("../../onlineforecast"))
    #?cache_name
    
    #' Caching of the value returned by a function
    #'
    #' Use it in the beginning of a function, which runs a time consuming calculation, like fitting a model using optimization.
    #'
    #' It makes a cache name, which can be used to save a unique cache file (see \code{\link{cache_save}()}).
    #'
    #' The \code{cache_name} function must receive all the objects (in \code{...}) which influence the value of the function. It simply calculates a checksum using the \code{digest} package.
    #' 
    #' Further, it finds the name of the calling function and its definition, such that if anything changes in the function definition, then the cache file name changes too.
    #'
    #' @title Generation of a name for a cache file for the value of a function.
    #' @param ... The objects from which to calculate cache file name.
    #' If no objects given, then all the objects of the calling function are used for generating the checksum for the file name.
    #' @param cachedir Path for saving the cache, i.e. prefixed to the generated name, remember to end with '/' to make a directory.
    #' @return A generated cache file name.
    #' @examples 
    #' # A function for demonstrating the using caching
    #' fun <- function(x, y){
    #'     # Generate the cache name (no argument given, so both x and y is used)
    
    pbac's avatar
    pbac committed
    #'     nm <- cache_name(cachedir=cachedir)
    
    pbac's avatar
    pbac committed
    #'     # If the result is cached, then just return it
    #'     if(file.exists(nm)){ return(readRDS(nm)) }
    #'     # Do the calculation
    #'     res <- x^2 + y + 1
    #'     # Wait 1 sec
    #'     Sys.sleep(1)
    #'     # Save for cache
    #'     cache_save(res, nm)
    #'     # Return
    #'     return(res)
    #' }
    
    pbac's avatar
    pbac committed
    #'
    #' # For this example use a temporary directory 
    #' # In real use this should not be temporary! (changes between R sessions with tempdir())
    #' cachedir <- tempdir()
    
    pbac's avatar
    pbac committed
    #'
    #' # Uncomment to run:
    
    pbac's avatar
    pbac committed
    #' # First time it takes at least 1 sec.
    
    pbac's avatar
    pbac committed
    #' #fun(x=2,y=2)
    
    pbac's avatar
    pbac committed
    #' # Second time it loads the cache and is much faster
    
    pbac's avatar
    pbac committed
    #' #fun(x=2,y=2)
    
    pbac's avatar
    pbac committed
    #' # Try changing the arguments (x,y) and run again
    #'
    #' # See the cache file(s)
    
    pbac's avatar
    pbac committed
    #' #dir(cachedir)
    
    pbac's avatar
    pbac committed
    #' # Delete the cache folder
    
    pbac's avatar
    pbac committed
    #' #unlink(cachedir, recursive=TRUE)
    
    pbac's avatar
    pbac committed
    #'
    #' # Demonstrate how cache_name() is functioning
    #' # Cache using the all objects given in the function calling, i.e. both x and y
    #' fun <- function(x,y){
    #'     x^2 + y + 1
    #'     return(cache_name())
    #' }
    #' # These are the same (same values)
    #' fun(x=1,y=2)
    #' fun(1,2)
    #' fun(y=2,x=1)
    #' # But this one is different
    #' fun(x=2,y=1)
    #'
    #' # Test: cache using the values specified in the cache_name call
    #' fun2 <- function(x,y){
    #'     x^2 + y + 1
    #'     return(cache_name(x))
    #' }
    #'
    #' # So now its only the x value that change the name
    #' fun2(1,2)
    #' fun2(1,3)
    #' # But this one is different 
    #' fun2(3,3)
    #' # And the function named changed the name
    #'
    #' @export
    cache_name <- function(..., cachedir = "cache"){
        # Get the name, definition and arguments of the function from which cache_name was called
        funname <- strsplit(deparse(sys.calls()[[sys.nframe()-1]]), "\\(")[[1]][1]
    
    pbac's avatar
    pbac committed
        # Find the function in the nearest environment in the stack (i.e. parent calls)
    
    pbac's avatar
    pbac committed
        ## for(i in rev(sys.parents())){
        ##     browser()
        ##     if(funname %in% ls(parent.frame(i+1))){
        ##         val <- mget(funname, parent.frame(i+1))
        ##         break
        ##     }
        ## }
        ## fundef <- digest::digest(attr(eval(val[[funname]]), "srcref"))
        # Somehow the above stopped working, don't know why! just take it, this should do the same I guess
    
    pbac's avatar
    pbac committed
        fundef <- try(get(funname), silent=TRUE)
        fundef <- digest::digest(fundef)
    
    pbac's avatar
    pbac committed
        # if no arguments were given, then use the arguments function from which cache_name was called
        if(length(list(...)) == 0){
    
            funargs <- digest::digest(as.list( match.call(definition = sys.function( -1 ), call = sys.call(-1)))[-1])
    
    pbac's avatar
    pbac committed
        }else{
            funargs <- digest::digest(list(...))
        }
        # Create the md5 checksum filename with digest
        filename <- paste0(funname,"_",fundef,"_",funargs,".RDS")
        return(gsub("//","/",paste0(cachedir,"/",filename)))
    }