Return 从 R 函数中列出 vs 环境

Return list vs environment from an R function

在以下两种情况下,有什么advantage/disadvantage可以使用一个而不是另一个? Case-I 将其输出作为环境返回,Case-II 将其输出作为列表返回。

案例一:

function(x) {
  ret <- new.env()
  ret$x <- x
  ret$y <- x^2
  return(ret)
}

案例二:

function(x) {
  ret <- list()
  ret$x <- x
  ret$y <- x^2
  return(ret)
}

虽然相似,但 return 列表和环境存在差异。 来自 Advanced R:

Generally, an environment is similar to a list, with four important exceptions:

  • Every name in an environment is unique.

  • The names in an environment are not ordered (i.e., it doesn’t make sense to ask what the first element of an environment is).

  • An environment has a parent.

  • Environments have reference semantics.

More technically, an environment is made up of two components, the frame, which contains the name-object bindings (and behaves much like a named list), and the parent environment. Unfortunately “frame” is used inconsistently in R. For example, parent.frame() doesn’t give you the parent frame of an environment. Instead, it gives you the calling environment. This is discussed in more detail in calling environments.

来自帮助:

help(new.env)

Environments consist of a frame, or collection of named objects, and a pointer to an enclosing environment. The most common example is the frame of variables local to a function call; its enclosure is the environment where the function was defined (unless changed subsequently). The enclosing environment is distinguished from the parent frame: the latter (returned by parent.frame) refers to the environment of the caller of a function. Since confusion is so easy, it is best never to use ‘parent’ in connection with an environment (despite the presence of the function parent.env).

来自函数的文档:

e1 <- new.env(parent = baseenv())  # this one has enclosure package:base.
e2 <- new.env(parent = e1)
assign("a", 3, envir = e1)
ls(e1)
#[1] "a"

然而 ls 将给出创建的环境:

ls()
#[1] "e1" "e2"

并且您可以像访问列表一样访问您的环境对象:

e1$a
#[1] 3

使用你的函数:

f1 <- function(x) {
   ret <- new.env()
   ret$x <- x
   ret$y <- x^2
   return(ret)
}

res <- f1(2)
res
#<environment: 0x0000021d55a8a3e8>

res$y
#[1] 4

f2 <- function(x) {
   ret <- list()
   ret$x <- x
   ret$y <- x^2
   return(ret)

res2 <- f(2)
res2
#$x
#[1] 2

#$y
#[1] 4

res2$y
#[1] 4

根据microbenchmarking:

,他们的表现非常相似
microbenchmark::microbenchmark(
   function(x) {
      ret <- new.env()
      ret$x <- x
      ret$y <- x^2
      return(ret)
   },
   function(x) {
      ret <- list()
      ret$x <- x
      ret$y <- x^2
      return(ret)
   },
   times = 500L
)

#Unit: nanoseconds
#                                                                                 #expr
# function(x) {     ret <- new.env()     ret$x <- x     ret$y <- x^2     #return(ret) }
#    function(x) {     ret <- list()     ret$x <- x     ret$y <- x^2     #return(ret) }
# min lq   mean median  uq  max neval
#   0  1 31.802      1 100  801   500
#   0  1 37.802      1 100 2902   500

它们 return 个大小相同的对象:

object.size(res)
#464 bytes

object.size(res2)
#464 bytes

并且您始终可以从环境 (list2env) 生成列表,反之亦然 (as.list):

L <- list(a = 1, b = 2:4, p = pi, ff = gl(3, 4, labels = LETTERS[1:3]))
e <- list2env(L)
e$ff
# [1] A A A A B B B B C C C C
#Levels: A B C

as.list(e)
#$ff
# [1] A A A A B B B B C C C C
#Levels: A B C
#
#$p
#[1] 3.141593
#
#$b
#[1] 2 3 4
#
#$a
#[1] 1