简要背景:许多(大多数?)当代广泛使用的编程语言至少有一些共同的adt[抽象数据类型],特别是,
string(由字符组成的序列)
list(值的有序集合),和
基于映射的类型(将键映射到值的无序数组)
在R编程语言中,前两个分别被实现为character
和vector
。
当我开始学习R时,有两件事几乎从一开始就很明显:list
是R中最重要的数据类型(因为它是R data.frame
的父类),第二,我只是不理解它们是如何工作的,至少不足以在我的代码中正确使用它们。
首先,在我看来R的list
数据类型是映射ADT的直接实现(Python中的dictionary
, Objective C中的NSMutableDictionary
, Perl和Ruby中的hash
, Javascript中的object literal
,等等)。
例如,创建它们就像创建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__abc0也是< em >与< / em >其他映射类型的adt(从我学过的语言中)。我猜这是S的初始规范的结果,也就是说,打算从头开始设计一种数据/统计DSL[领域特定语言]。
在广泛使用的其他语言中,R list
s和映射类型之间存在显著差异(例如,Python, Perl, JavaScript):
第一个, R中的__abc0是一个命令集合,就像向量一样,尽管值是键控的(即键可以是任何可哈希值,而不仅仅是连续的整数)。在其他语言中,映射数据类型几乎总是无序。
第二个, list
可以从函数返回,即使你在调用函数时从未传入list
,而尽管返回list
的函数不包含(显式)list
构造函数(当然,你可以在实践中通过调用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的list
的一个第三特殊特征:它们似乎不能成为另一个ADT的成员,如果你试图这样做,那么主容器将被强制转换为list
。例如,
x = c(0.5, 0.8, 0.23, list(0.5, 0.2, 0.9), recursive=TRUE)
class(x)
# [1] list
我在这里的意图不是批评这种语言或它是如何被记录的;同样,我并不是说list
数据结构或它的行为有任何错误。我所追求的是纠正我对它们如何工作的理解,这样我就可以在我的代码中正确地使用它们。
以下是我想更好地理解的事情:
确定函数调用何时返回list
(例如,上面提到的strsplit
表达式)的规则是什么?
如果我没有显式地为list
分配名称(例如,list(10,20,30,40)
)默认名称只是以1开头的连续整数吗?(我假设,但我远不确定答案是肯定的,否则我们将无法通过调用unlist
将这种类型的list
强制转换为一个向量。)
[]
和[[]]
返回相同结果?
x = list(1, 2, 3, 4)
两个表达式都返回"1":
x[1]
x[[1]]
< / p >
为什么这两个表达式不返回相同的结果?
x = list(1, 2, 3, 4)
x2 = list(1:4)
< / p >
请不要指给我R文档(?list
, R-intro
)——我已经仔细阅读了它,它不能帮助我回答我刚才背诵的问题类型。
(最后,我最近学习并开始使用一个名为hash
的R包(在CRAN上可用),它通过S4类实现了传统的映射类型行为;我当然可以推荐这套套餐。)