简要背景:许多(大多数?)当代广泛使用的编程语言至少有一些共同的adt[抽象数据类型],特别是,

字符串(由字符组成的序列) List(值的有序集合)和 基于映射的类型(将键映射到值的无序数组)

在R编程语言中,前两者分别实现为字符和向量。

当我开始学习R时,有两件事几乎从一开始就很明显:list是R中最重要的数据类型(因为它是R data.frame的父类),其次,我就是不理解它们是如何工作的,至少在我的代码中不能正确地使用它们。

首先,在我看来,R的列表数据类型是映射ADT的直接实现(Python中的字典,Objective C中的NSMutableDictionary, Perl和Ruby中的散列,Javascript中的对象文字,等等)。

例如,创建它们就像创建Python字典一样,通过将键值对传递给构造函数(在Python中是dict而不是list):

x = list("ev1"=10, "ev2"=15, "rv"="Group 1")

访问R List中的项就像访问Python字典中的项一样,例如x['ev1']。同样,你可以通过以下方法检索“键”或“值”:

names(x)    # fetch just the 'keys' of an R list
# [1] "ev1" "ev2" "rv"

unlist(x)   # fetch just the 'values' of an R list
#   ev1       ev2        rv 
#  "10"      "15" "Group 1" 

x = list("a"=6, "b"=9, "c"=3)  

sum(unlist(x))
# [1] 18

但R列表也不同于其他地图类型的adt(从我学过的语言中)。我猜这是S的初始规范的结果,也就是说,打算从头开始设计一种数据/统计DSL[领域特定语言]。

R列表与其他广泛使用的语言中的映射类型之间的三个显著区别(例如:Python, Perl, JavaScript):

首先,R中的列表是一个有序的集合,就像向量一样,即使值是键(即键可以是任何可哈希的值,而不仅仅是连续的整数)。其他语言中的映射数据类型几乎总是无序的。

其次,即使在调用函数时没有传入列表,即使返回列表的函数不包含(显式)列表构造函数,也可以从函数返回列表(当然,在实践中可以通过调用unlist来包装返回的结果来处理这个问题):

x = strsplit(LETTERS[1:10], "")     # passing in an object of type 'character'

class(x)                            # returns 'list', not a vector of length 2
# [1] list

R列表的第三个特殊特性:它们似乎不能成为另一个ADT的成员,如果您尝试这样做,那么主容器将被强制转换为列表。例如,

x = c(0.5, 0.8, 0.23, list(0.5, 0.2, 0.9), recursive=TRUE)

class(x)
# [1] list

我在这里的意图不是批评这种语言或它是如何被记录的;同样,我并不是说列表数据结构或它的行为有任何问题。我所追求的是纠正我对它们如何工作的理解,这样我就可以在我的代码中正确地使用它们。

以下是我想更好地理解的事情:

What are the rules which determine when a function call will return a list (e.g., strsplit expression recited above)? If I don't explicitly assign names to a list (e.g., list(10,20,30,40)) are the default names just sequential integers beginning with 1? (I assume, but I am far from certain that the answer is yes, otherwise we wouldn't be able to coerce this type of list to a vector w/ a call to unlist.) Why do these two different operators, [], and [[]], return the same result? x = list(1, 2, 3, 4) both expressions return "1": x[1] x[[1]] why do these two expressions not return the same result? x = list(1, 2, 3, 4) x2 = list(1:4)

请不要给我指R文档(?我已经仔细阅读了它,它并不能帮助我回答我刚才背诵的那种问题。

(最后,我最近学习并开始使用一个名为hash的R包(在CRAN上可用),它通过S4类实现了传统的映射类型行为;我当然可以推荐这套套餐。)


当前回答

如果有帮助的话,我倾向于把R中的“列表”想象成其他前oo语言中的“记录”:

它们没有对总体类型做任何假设(或者说,所有可能记录的任何arity和字段名的类型都是可用的)。 它们的字段可以是匿名的(然后按照严格的定义顺序访问它们)。

“record”这个名字会与数据库术语中“记录”(又名行)的标准含义相冲突,这可能就是为什么它们的名字本身就暗示了:作为列表(字段)。

其他回答

如果有帮助的话,我倾向于把R中的“列表”想象成其他前oo语言中的“记录”:

它们没有对总体类型做任何假设(或者说,所有可能记录的任何arity和字段名的类型都是可用的)。 它们的字段可以是匿名的(然后按照严格的定义顺序访问它们)。

“record”这个名字会与数据库术语中“记录”(又名行)的标准含义相冲突,这可能就是为什么它们的名字本身就暗示了:作为列表(字段)。

x = list(1, 2, 3, 4)
x2 = list(1:4)
all.equal(x,x2)

不一样,因为1:4等于c(1,2,3,4) 如果你想让它们相同,那么:

x = list(c(1,2,3,4))
x2 = list(1:4)
all.equal(x,x2)

只是为了解决你问题的最后一部分,因为这确实指出了R中列表和向量之间的区别:

为什么这两个表达式不返回相同的结果? X = list(1,2,3,4);X2 = list(1:4)

列表可以包含任何其他类作为每个元素。所以你可以有一个列表,其中第一个元素是一个字符向量,第二个是一个数据帧,等等。在本例中,您创建了两个不同的列表。X有四个向量,每个向量的长度都是1。X2有一个长度为4的向量:

> length(x[[1]])
[1] 1
> length(x2[[1]])
[1] 4

这是完全不同的列表。

R列表非常类似于哈希映射数据结构,因为每个索引值都可以与任何对象相关联。下面是一个简单的列表示例,它包含3个不同的类(包括一个函数):

> complicated.list <- list("a"=1:4, "b"=1:3, "c"=matrix(1:4, nrow=2), "d"=search)
> lapply(complicated.list, class)
$a
[1] "integer"
$b
[1] "integer"
$c
[1] "matrix"
$d
[1] "function"

假设最后一个元素是搜索函数,我可以这样调用它:

> complicated.list[["d"]]()
[1] ".GlobalEnv" ...

作为最后的评论:应该注意data.frame实际上是一个列表(来自data.frame文档):

数据帧是一组行数相同且行名唯一的变量,给定类"data.frame"

这就是为什么data.frame中的列可以有不同的数据类型,而矩阵中的列则不能。举个例子,在这里我尝试用数字和字符创建一个矩阵:

> a <- 1:4
> class(a)
[1] "integer"
> b <- c("a","b","c","d")
> d <- cbind(a, b)
> d
 a   b  
[1,] "1" "a"
[2,] "2" "b"
[3,] "3" "c"
[4,] "4" "d"
> class(d[,1])
[1] "character"

请注意,我无法将第一列的数据类型更改为数字,因为第二列有字符:

> d[,1] <- as.numeric(d[,1])
> class(d[,1])
[1] "character"

再补充一点:

R在散列包中确实有一个与Python dict等效的数据结构。你可以在这篇来自开放数据组的博客文章中读到它。这里有一个简单的例子:

> library(hash)
> h <- hash( keys=c('foo','bar','baz'), values=1:3 )
> h[c('foo','bar')]
<hash> containing 2 key-value pairs.
  bar : 2
  foo : 1

就可用性而言,哈希类非常类似于列表。但对于大型数据集,性能更好。

为什么这两个不同的操作符[]和[[]]返回相同的结果?

x = list(1, 2, 3, 4)

[ ] provides sub setting operation. In general sub set of any object will have the same type as the original object. Therefore, x[1] provides a list. Similarly x[1:2] is a subset of original list, therefore it is a list. Ex. x[1:2] [[1]] [1] 1 [[2]] [1] 2 [[ ]] is for extracting an element from the list. x[[1]] is valid and extract the first element from the list. x[[1:2]] is not valid as [[ ]] does not provide sub setting like [ ]. x[[2]] [1] 2 > x[[2:3]] Error in x[[2:3]] : subscript out of bounds