环境作为哈希映射
注意:在随后的段落中,术语哈希映射和哈希表可互换使用,并且指的是相同的概念 ,即通过使用内部哈希函数提供有效密钥查找的数据结构。
介绍
虽然 R 不提供本机哈希表结构,但是通过利用从 new.env
(默认情况下)返回的 environment
对象提供哈希键查找的事实,可以实现类似的功能。以下两个语句是等效的,因为 hash
参数默认为 TRUE
:
H <- new.env(hash = TRUE)
H <- new.env()
另外,可以通过 size
参数指定内部哈希表预先分配特定大小,size
参数的默认值为 29.与所有其他 R 对象一样,environment
s 管理自己的内存并根据需要增加容量,因此,虽然没有必要为 size
请求非默认值,但如果对象(最终)包含大量元素,则可能会有轻微的性能优势。值得注意的是,通过 size
分配额外空间本身并不会导致内存占用更大的对象:
object.size(new.env())
# 56 bytes
object.size(new.env(size = 10e4))
# 56 bytes
插入
可以使用为 environment
类提供的 [[<-
或 $<-
方法来完成元素的插入,但不能使用单括号赋值([<-
) :
H <- new.env()
H[["key"]] <- rnorm(1)
key2 <- "xyz"
H[[key2]] <- data.frame(x = 1:3, y = letters[1:3])
H$another_key <- matrix(rbinom(9, 1, 0.5) > 0, nrow = 3)
H["error"] <- 42
#Error in H["error"] <- 42 :
# object of type 'environment' is not subsettable
与 R 的其他方面一样,第一种方法(object[[key]] <- value
)通常优于第二种方法(object$key <- value
),因为在前一种情况下,可以使用变量而不是文字值(例如上面示例中的 key2
)。
与散列映射实现的情况一样,environment
对象不会存储重复键。尝试为现有密钥插入键值对将替换先前存储的值:
H[["key3"]] <- "original value"
H[["key3"]] <- "new value"
H[["key3"]]
#[1] "new value"
重点查找
同样,可以使用 [[
或 $
访问元素,但不能使用 [
访问:
H[["key"]]
#[1] 1.630631
H[[key2]] ## assuming key2 <- "xyz"
# x y
# 1 1 a
# 2 2 b
# 3 3 c
H$another_key
# [,1] [,2] [,3]
# [1,] TRUE TRUE TRUE
# [2,] FALSE FALSE FALSE
# [3,] TRUE TRUE TRUE
H[1]
#Error in H[1] : object of type 'environment' is not subsettable
检查哈希地图
作为一个普通的 environment
,哈希映射可以通过典型的方式进行检查:
names(H)
#[1] "another_key" "xyz" "key" "key3"
ls(H)
#[1] "another_key" "key" "key3" "xyz"
str(H)
#<environment: 0x7828228>
ls.str(H)
# another_key : logi [1:3, 1:3] TRUE FALSE TRUE TRUE FALSE TRUE ...
# key : num 1.63
# key3 : chr "new value"
# xyz : 'data.frame': 3 obs. of 2 variables:
# $ x: int 1 2 3
# $ y: chr "a" "b" "c"
可以使用 rm
删除元素:
rm(list = c("key", "key3"), envir = H)
ls.str(H)
# another_key : logi [1:3, 1:3] TRUE FALSE TRUE TRUE FALSE TRUE ...
# xyz : 'data.frame': 3 obs. of 2 variables:
# $ x: int 1 2 3
# $ y: chr "a" "b" "c"
灵活性
使用 environment
对象作为哈希表的一个主要好处是它能够将几乎任何类型的对象存储为值,甚至其他 environment
s :
H2 <- new.env()
H2[["a"]] <- LETTERS
H2[["b"]] <- as.list(x = 1:5, y = matrix(rnorm(10), 2))
H2[["c"]] <- head(mtcars, 3)
H2[["d"]] <- Sys.Date()
H2[["e"]] <- Sys.time()
H2[["f"]] <- (function() {
H3 <- new.env()
for (i in seq_along(names(H2))) {
H3[[names(H2)[i]]] <- H2[[names(H2)[i]]]
}
H3
})()
ls.str(H2)
# a : chr [1:26] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" ...
# b : List of 5
# $ : int 1
# $ : int 2
# $ : int 3
# $ : int 4
# $ : int 5
# c : 'data.frame': 3 obs. of 11 variables:
# $ mpg : num 21 21 22.8
# $ cyl : num 6 6 4
# $ disp: num 160 160 108
# $ hp : num 110 110 93
# $ drat: num 3.9 3.9 3.85
# $ wt : num 2.62 2.88 2.32
# $ qsec: num 16.5 17 18.6
# $ vs : num 0 0 1
# $ am : num 1 1 1
# $ gear: num 4 4 4
# $ carb: num 4 4 1
# d : Date[1:1], format: "2016-08-03"
# e : POSIXct[1:1], format: "2016-08-03 19:25:14"
# f : <environment: 0x91a7cb8>
ls.str(H2$f)
# a : chr [1:26] "A" "B" "C" "D" "E" "F" "G" "H" "I" "J" "K" ...
# b : List of 5
# $ : int 1
# $ : int 2
# $ : int 3
# $ : int 4
# $ : int 5
# c : 'data.frame': 3 obs. of 11 variables:
# $ mpg : num 21 21 22.8
# $ cyl : num 6 6 4
# $ disp: num 160 160 108
# $ hp : num 110 110 93
# $ drat: num 3.9 3.9 3.85
# $ wt : num 2.62 2.88 2.32
# $ qsec: num 16.5 17 18.6
# $ vs : num 0 0 1
# $ am : num 1 1 1
# $ gear: num 4 4 4
# $ carb: num 4 4 1
# d : Date[1:1], format: "2016-08-03"
# e : POSIXct[1:1], format: "2016-08-03 19:25:14"
限制
使用 environment
对象作为哈希映射的一个主要限制是,与 R 的许多方面不同,元素查找/插入不支持向量化:
names(H2)
#[1] "a" "b" "c" "d" "e" "f"
H2[[c("a", "b")]]
#Error in H2[[c("a", "b")]] :
# wrong arguments for subsetting an environment
Keys <- c("a", "b")
H2[[Keys]]
#Error in H2[[Keys]] : wrong arguments for subsetting an environment
根据存储在对象中的数据的性质,可以使用 vapply
或 list2env
一次分配多个元素:
E1 <- new.env()
invisible({
vapply(letters, function(x) {
E1[[x]] <- rnorm(1)
logical(0)
}, FUN.VALUE = logical(0))
})
all.equal(sort(names(E1)), letters)
#[1] TRUE
Keys <- letters
E2 <- list2env(
setNames(
as.list(rnorm(26)),
nm = Keys),
envir = NULL,
hash = TRUE
)
all.equal(sort(names(E2)), letters)
#[1] TRUE
上述两者都不是特别简洁,但是当键值对的数量很大时,可能优选使用 for
循环等。