R 中的list是有序的元素集合,它和vectors在几个方面有所不同:
numeric和character混在一起)。把它类比成其他语言里的东西并不准确。
R 的list与 Python 字典有些相似,不过list是有序的,可以通过数字下标来访问。
它也与 C 的struct有些相似,区别在于list可以在运行时轻松扩展。
给元素起名称(通常叫做“标签”),这种做法在列表中比在向量中更常见:
list(x = 5.3, y = 4.2, color = "blue")
#> $x
#> [1] 5.3
#> $y
#> [1] 4.2
#> $color
#> [1] "blue"
不过标签是可选的,这时 R 会直接使用编号下标:
list(5.3, 4.2, "blue")
#> [[1]]
#> [1] 5.3
#> [[2]]
#> [1] 4.2
#> [[3]]
#> [1] "blue"
有三种方法可以做到这一点,每种方法适用于不同的场景。
$tag表示法如果标签名事先已知,这种方式很常用。
point <- list(x = 5.3, y = 4.2, color = "blue")
#> point$x
#> [1] 5.3
#> point$color
#> [1] "blue"
[[tag]]表示法它的好处是标签名可以在运行时才决定。 注意这里用了双层方括号。
want <- "y"
point[[want]] # same as point$y
#> [1] 4.2
无论列表有没有标签,这种方式都适用。 同样,方括号要写两层。
point[[3]] # same as point$color
#> [1] "blue"
很简单:给某个元素赋值,如果它已存在就会修改它,如果它是新的就会插入它。
point$color <- "red"
point$trust <- TRUE
# str() gives more compact output than the default display
str(point)
#> List of 4
#> $ x : num 5.3
#> $ y : num 4.2
#> $ color: chr "red"
#> $ trust: logi TRUE
要删除元素,就给不想要的元素赋值NULL。
point$trust <- NULL
str(point)
#> List of 3
#> $ x : num 5.3
#> $ y : num 4.2
#> $ color: chr "red"
vector类在技术上被称为atomic vector,因为它只能存放原子值:numeric、character、boolean等等。
由于list可以包含其他列表,它也被称为recursive vector。
这既是一个术语命名上的技术要点,也提醒我们列表可以具有任意复杂的结构。 访问列表元素时,程序员有责任弄清楚会得到什么。
上面的一些例子用双层方括号[[ ]]来访问列表元素。
它会尝试把元素展平为其底层类型,如果你只想要一个数字或字符串,这会很有用。
使用单层方括号[ ]则不会做这种展平,返回的值会是一个子列表。
point[["color"]]
#> [1] "red" # flattened
point["color"]
#> $color
#> [1] "red" # sublist
即使子列表不是 R 新手通常想要的东西,也请留意这一点。
为了最大程度的灵活性,许多标准 R 函数会返回list,即使它像这里一样只包含一个原子值。
要把它转换成一个向量,有两种办法。
[[1]]取出第一个元素unlist()函数。pt <-point["color"]
pt[[1]]
#> [1] "red" # simple 1-element vector
unlist(pt)
#> color
#> "red" # 1-element vector with name
使用unlist()时要小心。
在这个简单的例子里,展平结构很有用,但当列表元素更复杂时,它可能会带来意想不到的结果。
list和原子vector一样,是连续存储的。区别在于,数值向量和布尔向量存储的是值,而列表存储的是指向内存中其他位置对象的引用。
了解这一点对 R 使用者来说并不太重要,但在像 Exercism 这样的多语言社区里,它可能对熟悉其他语言的程序员有帮助。