R中列表元素的递归操作

时间:2018-04-25 14:01:20

标签: r list recursion

我在R脚本的全局环境中有一个嵌套列表。

anno <- list()

anno[['100']] <- list(
    name = "PLACE",
    color = "#a6cee3",
    isDocumentAnnotation = T,
    sublist = list()
)

person_sublist <- list()

person_sublist[['200']] <- list(
  name = "ACTOR",
  color = "#7fc97f",
  isDocumentAnnotation = T,
  sublist = list()
)

person_sublist[['300']] <- list(
  name = "DIRECTOR",
  color = "#beaed4",
  isDocumentAnnotation = T,
  sublist = list()
)

anno[['400']] <- list(
  name = "PERSON",
  color = "#1f78b4",
  isDocumentAnnotation = T,
  sublist = person_sublist
)

在运行我的过程时,我通过id(100,200,...)以交互方式选择元素。作为回报,想要添加,删除或移动列表中的元素。

因此我想到使用递归函数来浏览列表:

searchListId <- function(parent_id = NULL, annotation_system = NULL)
{
  for(id in names(annotation_system))
  {
    cat(paste(id,"\n"))

    if(id == parent_id)
    {
      return(annotation_system[[id]]$sublist)
    }
    else
    {
      if(length(annotation_system[[id]]$sublist) > 0)
      {
        el <- searchListId(parent_id, annotation_system[[id]]$sublist)
        if(!is.null(el))
          return(el)
      }

    }
  }

  return(NULL)
}

searchListId('100', anno)

此函数返回在&#39; anno&#39; -list中匹配元素的子列表元素中找到的list()。我的问题是R的全局环境。如果我操纵某些东西(删除,添加,移动返回的子列表中的内容),我需要用<<-重置全局变量。但是在递归函数的情况下,我只在parent_id匹配的上下文中保存当前子列表。如何在通过递归函数导航时在R中引用全局嵌套列表?这在R中是否可能?

我想要执行的调用,以便删除,添加或移动列表中的元素&#39; anno&#39;是:

deleteListId('100', anno) #Should return the list without the element 100
addListId('400', anno) #Should return the list with a new element nested in '400'
switchListId('400','200', anno) #Should return a list where the elements with the according keys are switched.

但棘手的部分是我不知道递归结构的深度。通常我会使用元素引用来直接操作它们但是如果我想使用递归,如何在R中操作嵌套列表的解决方案呢?

1 个答案:

答案 0 :(得分:2)

如果可能,让递归函数获取列表,更改它,并返回新版本。我之所以这样说是因为它的惯用语R. R倾向于成为一种功能性语言,其中一部分意味着基于国家的行为是不受欢迎的。一般来说,函数应该只修改状态,如果他们所做的那样。例如,scale(x)不会影响x变量中存储的值。但x <- scale(x)确实如此,因为<-函数(是的,它是一个函数)是为了修改状态。

此外,不要担心内存,除非你知道根据过去的经验这将是一个问题。在幕后,R非常善于防止不必要的复制,所以相信它做正确的事情。这使您可以使用更简单的心理模型。

如何递归修改列表而不影响原始列表的框架:

anno <- list()

anno[['A1']] <- list(
  sublist = list(
    A3 = list(sublist = NULL),
    A4 = list(sublist = list(A6 = list(sublist = NULL))),
    A5 = list(sublist = NULL)
  )
)

change_list <- function(x) {
  for (i in seq_along(x)) {
    value <- x[[i]]
    if (is.list(value)) {
      x[[i]] <- change_list(value)
    } else {
      if (is.null(value)) {
        x[[i]] <- "this ws null"
      }
    }
  }
  x
}

change_list(anno)
# $A1
# $A1$sublist
# $A1$sublist$A3
# $A1$sublist$A3$sublist
# [1] "something new"
# 
# 
# $A1$sublist$A4
# $A1$sublist$A4$sublist
# $A1$sublist$A4$sublist$A6
# $A1$sublist$A4$sublist$A6$sublist
# [1] "something new"
# 
# 
# 
# 
# $A1$sublist$A5
# $A1$sublist$A5$sublist
# [1] "something new"

如果您绝对需要修改全局命名空间中的项目,请使用环境而不是列表。

anno_env <- new.env()
anno_env[["A1"]] <- new.env()
anno_env[["A1"]][["sublist"]] <- new.env()
anno_env[["A1"]][["sublist"]][["A3"]] <- NULL
anno_env[["A1"]][["sublist"]][["A4"]] <- NULL

change_environment <- function(environ) {
  for (varname in ls(envir = environ)) {
    value <- environ[[varname]]
    if (is.environment(value)) {
      change_environment(value)
    } else {
      environ[[varname]] <- "something new"
    }
  }
}

change_environment(anno_env)

anno_env[["A1"]][["sublist"]][["A3"]]
# [1] "something new"