【问题标题】:How do you manipulate a list of unknown name in R without using parse?如何在不使用解析的情况下操作 R 中的未知名称列表?
【发布时间】:2018-04-16 09:23:33
【问题描述】:

我正在考虑将我的问题的数据(向量、列表等)和代码(函数)保存在树结构中(...的列表列表)。我不想为根节点指定名称,也不想为下一级节点指定名称。根节点下方的列表是彼此的不同版本,我希望能够以不同的方式比较它们,并以不同的方式构建它们,并赋予它们不同的任意名称。我目前正在使用以下内容来构建整体结构:

foo <- function(ref.txt, val.txt) eval(parse(text=paste0(ref.txt, ' <<- ', val.txt)))

一个简单的例子可能是:

root = list()
foo('root$v1', '42')
foo('root$v2', '43')
root
# $v1
# [1] 42
#
# $v2
# [1] 43

稍微不那么琐碎,继续上一个示例:

v3 <- c(42, 43)
foo('root$v3', 'v3')
root
# $v1
# [1] 42
#
# $v2
# [1] 43
#
# $v3
# [1] 42 43

同样,我不能硬编码,例如root$v3 &lt;- v3,因为直到运行时我才知道列表根的名称或下一级节点的名称。

我要求替代方案的部分原因是 @'Joris Meys 在 Stack Overflow 文章“Why doesn't assign() values to a list element work in R?”中的评论,他显然引用了 Lumley 的帖子“Re: [R] RE: Using a number as a name to access a list”。这些建议避免使用parse。但是,如果我直到运行时才知道名称,甚至不知道 path 的深度(请参阅 Lumley),如何避免 parse 可能?

【问题讨论】:

标签: r list parsing tree assign


【解决方案1】:

为您的root 列表添加一个参数怎么样?没有paste 诡计,没有eval 诡计,也不需要使用&lt;&lt;-,您通常应该避免使用...

foo <- function(lst, ref, val) { lst[[ref]] <- val; return(lst) }

root <- list()
root <- foo(root, "v1", 42)
root <- foo(root, "v2", 43)
root

v3 <- c(42, 43)
root <- foo(root, "v3", v3)
root

基于 cmets 编辑:这是一个将值分配给嵌套列表的任意条目的函数。 ref 参数应该是一个索引向量,每个级别一个:

foo <- function(lst, ref, val) {

  lvl <- length(ref)

  # extract the list at depth lvl - 1 from lst,
  # add val to this list and replace val with it,
  # repeat, now descending one level less deep,
  # and so on, until reaching the top level

  for (i in seq_len(lvl)) {

    res <- lst
    for (j in seq_len(lvl - i)) res <- res[[ref[j]]]
    res[[ref[lvl - i + 1]]] <- val
    val <- res

  }

  return(res)

}

(root <- list(a = list(a = 1, b = list(a = 1, b = 2)),
              b = list(a = 1), c = 3))

## $a
## $a$a
## [1] 1
## 
## $a$b
## $a$b$a
## [1] 1
## 
## $a$b$b
## [1] 2
## 
## 
## 
## $b
## $b$a
## [1] 1
## 
## 
## $c
## [1] 3

foo(lst = root, ref = c("a", "b", "c"), val = 3)

## $a
## $a$a
## [1] 1
## 
## $a$b
## $a$b$a
## [1] 1
## 
## $a$b$b
## [1] 2
## 
## $a$b$c
## [1] 3
## 
## 
## 
## $b
## $b$a
## [1] 1
## 
## 
## $c
## [1] 3

最后,这是一个将我的函数与parse + eval 进行比较的基准。使用三层嵌套,我的函数明显更快,但这可能会随着不同的列表结构而改变:

bar <- function(lst, ref, val) {

  eval(parse(text = paste(paste(c("lst", ref), collapse = "$"), "<- val")))
  return(lst)

}

library(microbenchmark)
microbenchmark(foo(lst = root, ref = c("a", "b", "c"), val = 3),
               bar(lst = root, ref = c("a", "b", "c"), val = 3))

## Unit: microseconds
##                                              expr     min       lq
##  foo(lst = root, ref = c("a", "b", "c"), val = 3)  47.089  48.6700
##  bar(lst = root, ref = c("a", "b", "c"), val = 3) 127.401 128.9505
##       mean  median       uq     max neval
##   55.98703  50.795  53.0640 191.575   100
##  134.71502 130.325 132.1755 291.400   100

【讨论】:

  • 这个精神完全正确——不需要&lt;&lt;-eval等。我确实怀疑是否需要一个函数——你的函数本质上是@987654333的副本@。 foo(root, "v1", 42)root[["v1"]] &lt;- 42 好吗?我想这取决于 OP。
  • 你说得对,我只是想保持原始代码的精神,假设它是一个最小的示例,并且该函数实际上所做的不仅仅是将值放入列表中。
  • 回复:@Gregor。假设root &lt;- list(A=list(B=list(C=list())))。我会使用root$A$B$C &lt;- bar 来替换root 下面三个级别的内容。我也可以使用root[['A']][['B']][['C']] &lt;- bar。除了在运行时我不知道我需要分配的深度,所以我什至不知道要使用多少 [[,更不用说在 [[]] 之间放置哪些字符.是否还有其他我不知道的符号,例如 [['A','B','C']]
  • 回复:@Gregor。我在哪里可以找到[[&lt;- 的文档?我已经查看了语言定义(没有提及)和帮助(没有帮助)。堆栈溢出搜索 "[[&lt;-" [r] 不会产生任何结果。我将针对那个问题开始一个新问题。
  • ?`[[&lt;-` 在 R 中就可以了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-04-24
  • 1970-01-01
  • 1970-01-01
  • 2012-10-31
  • 2022-11-22
  • 2018-10-21
  • 1970-01-01
相关资源
最近更新 更多