R 中的list是有序的項目集合,和vectors有幾點不同:
numeric和character混在一起)。和其他語言類比並不精確。R 的list有點像 Python 的字典,但list是有序的,而且可以用數值索引存取。它也有點像 C 的struct,差別在於list可以輕鬆地在執行階段擴充。
為元素加上名稱(常稱為「標籤」),在 list 中比在 vector 中更常見:
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
不論 list 有沒有標籤,這種方式都能用。同樣地,這裡的括號要寫成雙重。
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可以包含其他的 list,所以也稱為recursive vector。
這除了是術語命名上的技術重點,也提醒我們 list 可以有任意複雜的結構。存取 list 元素時會拿到什麼,得由程式設計者自己負責弄清楚。
前面有些例子用了雙重方括號[[ ]]來存取 list 元素。這種寫法會試著把元素攤平成它底層的型態,如果你只想要一個數字或字串,就很方便。
使用單一括號[ ]則不會攤平,回傳的值會是一個子 list。
point[["color"]]
#> [1] "red" # flattened
point["color"]
#> $color
#> [1] "red" # sublist
即使子 list 不是 R 新手會想要的結果,也請留意這種行為。為了最大的彈性,許多 R 的標準函式會回傳list,即使它像這裡一樣只包含單一個原子值。
要轉換成 vector 有兩種做法。
[[1]]取出第一個元素unlist()函式pt <-point["color"]
pt[[1]]
#> [1] "red" # simple 1-element vector
unlist(pt)
#> color
#> "red" # 1-element vector with name
使用unlist()時要小心喔。在這個簡單的例子裡,攤平結構很方便,但如果 list 元素比較複雜,就可能帶來意想不到的結果。
list和原子vector一樣是連續儲存的。差別在於,數值向量和布林向量儲存的是值,而 list 儲存的是記憶體中其他位置物件的參照。
對 R 使用者來說,知道這件事通常不太重要;但在像 Exercism 這樣的多語言社群裡,對熟悉其他語言的程式設計者可能會有幫助。