简体   繁体   中英

Logic to Cache a inverse of matrix in r

It is actually not a question but instead I couldn't understand the logic of the below code, can you please someone help me understand the logic of this code

makecachematrix<- function(x = matrix){
    inv <- NULL
    set <- function(y){
        x <<- y
        inv <<- NULL
    }
    get <- function(){
        x
    }
    setinv <- function(inverse){
        inv <<- inverse
    }
    getinv <- function(){
        inv
    }
    list(set = set, get = get, setinv = setinv, getinv = getinv)
}

cachesolve <- function(x,...){
    inv <- x$getinv()
    if(!is.null(inv)){
       message("getting cached data")
       return(inv)
     }
    mat.data <- x$get()
    inv <- solve(mat.data, ...)
    x$setinv(inv)
    return(inv)
}

test <- function(mat){
     temp <- makecachematrix(mat)
     st <- Sys.time()
     cachesolve(temp)
     et <- Sys.time()
     TimeTaken <- st - et
     return(TimeTaken)

     st <- Sys.time()
     cachesolve(temp)
     et <- Sys.time()
     TimeTaken <- st - et
     return(TimeTaken)

}

set.seed(101)
r <- rnorm(1000000)
mat1 <- matrix(r, 1000, 1000)
test(mat1)

From this code I can understand that it is caching the inverse of the matrix in order to reuse it but I couldn't understand the underlying logic of this script.

Sorry if it is silly question since I am beginner in R, so please help me.
Thank you in advance.

Let's start by saying how this should have been done: defining a reference class. Read ?setRefClass to see how that would be done.

On to how this works: scoping rules. When a function is defined, it remembers the environment in which is was created.

mat <- makecachematrix(matrix(1:4, 2))
ls(envir = environment(mat$set))
#[1] "get"    "getinv" "inv"    "set"    "setinv" "x"

Environments can have parent environments, and all variables in those parent environments will be available. It's why this function can work:

a <- 10
myfun <- function(x) x + a
myfun(5)
# [1] 15

So, even though the $set function only takes y as a parameter, it "knows" about all the variables defined in the makecachematrix function's environment. When x <<- y is used for assignment, it first looks for x at the same environment level. If it doesn't find it, it moves up a level and repeats the process. Once it finds x , it assigns it at that level. (Side note: if no x exists, one will be created in the global environment.)

What makecachematrix is doing is creating a list of functions, all of which have the same parent environment. So when one function alters a variable in that parent environment, that variable will also be altered for the other functions.

Yes, this can get complicated. That's why this type of thing should use the reference class system. Here's a basic version of that:

cache_matrix <- setRefClass("cache_matrix",
  fields = list(
    data     = "matrix",
    inverse  = "matrix"
  ),
  methods = list(
    get_inverse = function() {
      if (identical(length(.self$inverse), 0L)) {
        inverse <<- solve(.self$data)
      }
      cache_matrix(data = .self$inverse, inverse = .self$data)
    }
  )
)

Behind the scenes, it works pretty much the same way as makecachematrix : create an object with methods, all sharing an environment. But it's better to use provided systems than exploiting scoping rules to make your own.

Here's how to use the reference class.

mat <- cache_matrix(data = matrix(1:4, 2))
mat
# Reference class object of class "cache_matrix"
# Field "data":
#      [,1] [,2]
# [1,]    1    3
# [2,]    2    4
# Field "inverse":
# <0 x 0 matrix>

mat$data
#       [,1] [,2]
# [1,]    1    3
# [2,]    2    4

mat$get_inverse()
# Reference class object of class "cache_matrix"
# Field "data":
#      [,1] [,2]
# [1,]   -2  1.5
# [2,]    1 -0.5
# Field "inverse":
#      [,1] [,2]
# [1,]    1    3
# [2,]    2    4

mat
# Reference class object of class "cache_matrix"
# Field "data":
#      [,1] [,2]
# [1,]    1    3
# [2,]    2    4
# Field "inverse":
#      [,1] [,2]
# [1,]   -2  1.5
# [2,]    1 -0.5

mat$get_inverse()
# Reference class object of class "cache_matrix"
# Field "data":
#      [,1] [,2]
# [1,]   -2  1.5
# [2,]    1 -0.5
# Field "inverse":
#      [,1] [,2]
# [1,]    1    3
# [2,]    2    4

Bonus with classes: with some extra code, you can treat objects like normal matrices.

setMethod(
  "solve",
  "cache_matrix",
  function(a, b, ...) {
    a$get_inverse()
  }
)

solve(mat)
# Reference class object of class "cache_matrix"
# Field "data":
#      [,1] [,2]
# [1,]   -2  1.5
# [2,]    1 -0.5
# Field "inverse":
#      [,1] [,2]
# [1,]    1    3
# [2,]    2    4

setMethod(
  "Arith",
  "cache_matrix",
  function(e1, e2) {
    result <- callGeneric(e1$data, e2)
    cache_matrix(data = result)
  }
)

mat + 1
# Reference class object of class "cache_matrix"
# Field "data":
#      [,1] [,2]
# [1,]    2    4
# [2,]    3    5
# Field "inverse":
# <0 x 0 matrix>

setMethod(
  "Arith",
  "numeric",
  function(e1, e2) {
    if (inherits(e2, "cache_matrix")) e2 <- e2$data
    result <- callGeneric(e1, e2)
    cache_matrix(data = result)
  }
)

5 + mat
# Reference class object of class "cache_matrix"
# Field "data":
#       [,1] [,2]
# [1,]    6    8
# [2,]    7    9
# Field "inverse":
# <0 x 0 matrix>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM