diff --git a/DESCRIPTION b/DESCRIPTION index 94b3e04..d652cff 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -19,6 +19,7 @@ Suggests: aws.s3, httr, covr, + googleAuthR, googleCloudStorageR License: MIT + file LICENSE RoxygenNote: 6.1.0 diff --git a/R/memoise.R b/R/memoise.R index 48915d5..01dd1ce 100644 --- a/R/memoise.R +++ b/R/memoise.R @@ -118,9 +118,21 @@ memoise <- memoize <- function(f, ..., envir = environment(f), cache = cache_mem # That has not been called default_args <- default_args[setdiff(names(default_args), names(called_args))] - # Evaluate all the arguments - args <- c(lapply(called_args, eval, parent.frame()), - lapply(default_args, eval, envir = environment())) + # Evaluate called arguments + called_args <- lapply(called_args, eval, parent.frame()) + + # Emulate how R evaluate default arguments + emu_env <- new.env(parent = if (is.null(environment(encl$`_f`))) baseenv() else environment(encl$`_f`)) + for (n in setdiff(names(called_args), "")) assign(n, called_args[[n]], envir = emu_env) + for (n in names(default_args)) eval(bquote(delayedAssign(n, .(expr), eval.env= emu_env , assign.env = emu_env), + list(expr = default_args[[n]]))) + default_args <- sapply(names(default_args), get, envir = emu_env, simplify = FALSE) + + # All arguments + args <- c(called_args, default_args) + + # Replace memoised functions in arguments with their original bodies + args <- lapply(args, function(x) if (memoise::is.memoised(x)) as.character(body(environment(x)$`_f`)) else x) hash <- encl$`_cache`$digest( c(as.character(body(encl$`_f`)), args, @@ -147,7 +159,7 @@ memoise <- memoize <- function(f, ..., envir = environment(f), cache = cache_mem # This should only happen for primitive functions if (is.null(envir)) { - envir <- baseenv() + envir <- baseenv() } memo_f_env <- new.env(parent = envir) @@ -261,7 +273,7 @@ has_cache <- function(f) { # Modify the function body of the function to simply return TRUE and FALSE # rather than get or set the results of the cache body <- body(f) - body[[9]] <- quote(if (encl$`_cache`$has_key(hash)) return(TRUE) else return(FALSE)) + body[[15]] <- quote(if (encl$`_cache`$has_key(hash)) return(TRUE) else return(FALSE)) body(f) <- body f @@ -288,7 +300,7 @@ drop_cache <- function(f) { # Modify the function body of the function to simply drop the key # and return TRUE if successfully removed body <- body(f) - body[[9]] <- quote(if (encl$`_cache`$has_key(hash)) { + body[[15]] <- quote(if (encl$`_cache`$has_key(hash)) { encl$`_cache`$drop_key(hash) return(TRUE) } else { diff --git a/tests/testthat/test-memoise.R b/tests/testthat/test-memoise.R index 89ccac1..5c867e2 100644 --- a/tests/testthat/test-memoise.R +++ b/tests/testthat/test-memoise.R @@ -246,6 +246,39 @@ test_that("argument names don't clash with names in memoised function body", { expect_identical(f(1, 2, 3, 4, 5, 6, 7, 8, 9, 10), f_mem(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)) }) +test_that("default values dont't clash with names in memoised function body", { + f <- function(extra = list(`_f`, `_cache`, `_additional`, + mc, encl, called_args, default_args)) { + i <<- i + 1; i + } + `_f` <- `_cache` <- `_additional` <- mc <- encl <- called_args <- default_args <- 0 + i <- 0 + + fm <- memoise(f) + + expect_equal(f(), 1) + expect_equal(fm(), 2) + expect_equal(fm(), 2) + + `_f` <- `_cache` <- `_additional` <- mc <- encl <- called_args <- default_args <- 1 + expect_equal(fm(), 3) +}) + +test_that("other memoised function passed as arguments", { + f <- function(x) x + g <- function(fn) {i <<- fn(i) + 1; i} + i <- 0 + + fm <- memoise(f) + gm <- memoise(g) + + expect_equal(g(fm), 1) + expect_equal(gm(fm), 2) + expect_equal(gm(fm), 2) + expect_equal(g(fm), 3) + expect_equal(gm(fm), 2) +}) + context("has_cache") test_that("it works as expected with memoised functions", { mem_sum <- memoise(sum)