查找和写入环境

Finding and writing environment

我正在编写与 Python 'pop' 方法等效的 R。我知道第 99 个百分位数有一个,但我更喜欢我自己的(practice/understanding/consistency 等)。 作为参考,pop() 接受一个对象并从对象中删除第一项,同时返回它。所以

> l <- c(1,3,5)
> x <- pop(l)
> print(l)
> 3, 5
> print(x)
> 1

我正在使用 assign() 将输入对象替换为第一个值减一并从函数返回所述第一个值。

我的问题是,如何获取输入对象的环境并在 assign() 中使用该环境? 我试过使用 pryr::where() which returns 'R_GlobalEnv' 但我不能在 assign() 中使用这个值。相反,我可以在 assign() 中工作的唯一值是 'globalenv()'.

通过手机发布,如有问题请告诉我。

您可以使用 pryr::promise_info(l)$env 来完成,但这是一件非常 un-R-like 的事情。函数不应该有副作用。

例如,

    pop <- function(l) {
      info <- pryr::promise_info(l)
      if (!is.name(info$code))
        stop("Argument expression should be a name.")
      result <- l[[1]]  # work on lists too
      assign(as.character(info$code), l[-1], envir = info$env)
      result
    }
    l <- c(1, 3, 5)
    pop(l)
#> Registered S3 method overwritten by 'pryr':
#>   method      from
#>   print.bytes Rcpp
#> [1] 1
    l
#> [1] 3 5

reprex package (v0.3.0)

于 2020-08-15 创建

编辑添加:有趣的是,到目前为止,三个答案中的 none 适用于像这样的复杂情况:

f <- function(x) {
  cat("The pop(x) result is", pop(x), "\n")
  cat("Now x is ", x, "\n")
  cat("Now l is ", l, "\n")
}

l <- c(1, 3, 5)
f(l)

@RuiBarradas 的回答给出了

The pop(x) result is 5 
Now x is  1 3 5 
Now l is  1 3 5 

(他弹出最后一个值而不是第一个,这没什么大不了的,但是 xl 都没有被修改。)

@AllanCameron 的回答给出了

The pop(x) result is 1 
Now x is  3 5 
Now l is  1 3 5 

这可以说是正确的(x 被弹出),但我认为 l 被弹出会很好,但这似乎很棘手。

我的回答因这条消息而消失:

Error in pop(x) : Argument expression should be a name.

这似乎是一个错误:显然,无论是 x 还是 l,它确实是一个名称。问题似乎出在 pryr::promise_info 中,其中 return 是 return 值 x 的编译代码,而不仅仅是 x 的代码。如果我通过 compiler::enableJIT(0) 关闭 JIT 编译,我会得到与 @AllanCameron 相同的结果。我不清楚如何放回适当的数量以弹出 l 而不仅仅是 x.

下面的答案是基于这个R-Help post, function pop with function getEnvOf from this SO post,都适应了问题的问题。

getEnvOf <- function(what, which=rev(sys.parents())) {
  what <- as.character(substitute(what))
  for (frame in which)
    if (exists(what, frame=frame, inherits=FALSE))
      return(sys.frame(frame))
  return(NULL)
}
pop <- function(x){
  y <- as.character(substitute(x))
  e <- getEnvOf(y)
  if(length(x) > 0) {
    val <- x[[length(x)]]
    assign(y, x[-length(x)], envir = parent.env(e))
    val
  } else {
    msg <- paste(sQuote(y), "length is not > 0")
    warning(msg)
    NULL
  }
}

y <- c(1,3,5)
pop(y)

这也适用于列表。

z <- list(1, 2, 5)
pop(z)

w <- list(1, c(2, 4, 6), 5)
pop(w)
#[1] 5

pop(w)
#[1] 2 4 6

pop(w)
#[1] 1

pop(w)
#NULL
#Warning message:
#In pop(w) : ‘w’ length is not > 0

您可以在基础 R 中实现它,但不建议这样做。 R 是一种函数式语言,end-users 不期望具有副作用的函数。

pop <- function(vec)
{
  vec_name <- deparse(substitute(vec))
  assign(vec_name, vec[-1], envir = parent.frame())
  vec[1]
}

a <- c(2, 7, 9)

a
#> [1] 2 7 9

pop(a)
#> [1] 2

a
#> [1] 7 9

pop(a)
#> [1] 7

a
#> [1] 9

reprex package (v0.3.0)

于 2020-08-15 创建