1<%@meta language="R-vignette" content="-------------------------------- 2%\VignetteIndexEntry{List Environments} 3%\VignetteAuthor{Henrik Bengtsson} 4%\VignetteKeyword{R} 5%\VignetteKeyword{package} 6%\VignetteKeyword{vignette} 7%\VignetteKeyword{listenv} 8%\VignetteEngine{R.rsp::rsp} 9%\VignetteTangle{FALSE} 10--------------------------------------------------------------------"%> 11<% 12R.utils::use("R.utils") 13use("listenv") 14options("withCapture/newline" = FALSE) 15%> 16# <%@meta name="title"%> 17 18## Summary 19 20_List environments_ are environments that have list-like properties. They are implemented by the [listenv] package. The main features of a list environment are summarized in the below table: 21 22| Property | list environments | lists | environments | 23|------------------------------------------------------------------------------|:-----------------:|:------:|:------------:| 24| Number of elements, e.g. `length()` | yes | yes | yes | 25| Named elements, e.g. `names()`, `x$a` and `x[["a"]]` | yes | yes | yes | 26| Duplicated names | yes | yes | | 27| Element names are optional | yes | yes | | 28| Indexed elements, e.g. `x[[4]]` | yes | yes | | 29| Dimensions, e.g. `dim(x)` | yes | yes | | 30| Names of dimensions, e.g. `dimnames(x)` | yes | yes | | 31| Indexing by dimensions, e.g. `x[[2, 4]]` and `x[[2, "D"]]` | yes | yes | | 32| Multi-element subsetting, e.g. `x[c("a", "c")]`, `x[-1]` and `x[2:1, , 3]` | yes | yes | | 33| Multi-element subsetting preserves element names | yes | | | 34| Removing elements by assigning NULL, e.g. `x$c <- NULL` and `x[1:3] <- NULL` | yes | yes | | 35| Removing parts of dimensions by assigning NULL, e.g. `x[,2] <- NULL` | yes | | | 36| Mutable, e.g. `y <- x; y$a <- 3; identical(y, x)` | yes | | yes | 37| Compatible* with `assign()`, `delayedAssign()`, `get()` and `exists()` | yes | | yes | 38 39For example, 40```r 41<%=withCapture({ 42x <- listenv(a = 1, b = 2, c = "hello") 43x 44 45length(x) 46names(x) 47x$a 48x[[3]] <- toupper(x[[3]]) 49x$c 50 51y <- x 52y$d <- y$a + y[["b"]] 53names(y)[2] <- "a" 54y$a 55y 56identical(y, x) 57 58for (ii in seq_along(x)) { 59 cat(sprintf("Element %d (%s): %s\n", ii, sQuote(names(x)[ii]), x[[ii]])) 60} 61 62x[c(1, 3)] <- list(2, "Hello world!") 63 64x 65y <- as.list(x) 66str(y) 67z <- as.listenv(y) 68z 69identical(z, x) 70all.equal(z, x) 71})%> 72``` 73 74## Creating list environments 75List environments are created similarly to lists but also similarly to environments. To create an empty list environment, use 76```r 77<%=withCapture({ 78x <- listenv() 79x 80})%> 81``` 82This can later can be populated using named assignments, 83```r 84<%=withCapture({ 85x$a <- 1 86x 87})%> 88``` 89comparable to how both lists and environments work. Similarly to lists, they can also be populated using indices, e.g. 90```r 91<%=withCapture({ 92x[[2]] <- 2 93x$c <- 3 94x 95})%> 96``` 97Just as for lists, a list environment is expanded with `NULL` elements whenever a new element is added that is beyond the current length plus one, e.g. 98```r 99<%=withCapture({ 100x[[5]] <- 5 101x 102x[[4]] 103})%> 104``` 105 106As with lists, the above list environment can also be created from the start, e.g. 107```r 108<%=withCapture({ 109x <- listenv(a = 1, 3, c = 4, NULL, 5) 110x 111})%> 112``` 113 114 115As for lists, the length of a list environment can at any time be increased or decreased by assigning it a new length. 116If decreased, elements are dropped, e.g. 117```r 118<%=withCapture({ 119x 120length(x) <- 2 121x 122x[[1]] 123x[[2]] 124})%> 125``` 126If increased, new elements are populated with unnamed elements of `NULL`, e.g. 127```r 128<%=withCapture({ 129length(x) <- 4 130x 131x[[3]] 132x[[4]] 133})%> 134``` 135 136To allocate an "empty" list environment (with all `NULL`:s) of a given length, do 137```r 138<%=withCapture({ 139x <- listenv() 140length(x) <- 4 141x 142})%> 143``` 144_Note_: Unfortunately, it is _not_ possible to use `x <- vector("listenv", length = 4)`; that construct is only supported for the basic data types. 145 146Elements can be dropped by assigning `NULL`, e.g. to drop the first and third element of a list environment, do: 147```r 148<%=withCapture({ 149x[c(1, 3)] <- NULL 150x 151})%> 152``` 153 154 155## Iterating over elements 156 157### Iterating over elements by names 158Analogously to lists and plain environments, it is possible to iterate over elements of list environments by the element names. For example, 159```r 160<%=withCapture({ 161x <- listenv(a = 1, b = 2, c = 3) 162for (name in names(x)) { 163 cat(sprintf("Element %s: %s\n", sQuote(name), x[[name]])) 164} 165})%> 166``` 167 168### Iterating over elements by indices 169Analogously to lists, but contrary to plain environments, it is also possible to iterate over elements by their indices. For example, 170```r 171<%=withCapture({ 172x <- listenv(a = 1, b = 2, c = 3) 173for (ii in seq_along(x)) { 174 cat(sprintf("Element %d: %s\n", ii, x[[ii]])) 175} 176})%> 177``` 178 179 180## Coercion to and from list environments 181 182### Coercing to lists and vectors 183 184Coercing a list environment to a list: 185```r 186<%=withCapture({ 187x <- listenv(a = 2, b = 3, c = "hello") 188x 189y <- as.list(x) 190str(y) 191})%> 192``` 193 194Coercing a list to a list environment: 195```r 196<%=withCapture({ 197z <- as.listenv(y) 198z 199identical(z, x) 200all.equal(z, x) 201})%> 202``` 203 204Unlisting: 205```r 206<%=withCapture({ 207unlist(x) 208unlist(x[-3]) 209unlist(x[1:2], use.names=FALSE) 210})%> 211``` 212 213 214## Multi-dimensional list environments 215 216Analogously to lists, and contrary to plain environments, list environments can have dimensions with corresponding names. For example, 217```r 218<%=withCapture({ 219x <- as.listenv(1:6) 220dim(x) <- c(2, 3) 221dimnames(x) <- list(c("a", "b"), c("A", "B","C")) 222x 223})%> 224``` 225An easy way to quickly get an overview is to coerce to a list, e.g. 226```r 227<%=withCapture({ 228as.list(x) 229})%> 230``` 231Individual elements of a list environment can be accessed using standard subsetting syntax, e.g. 232```r 233<%=withCapture({ 234x[["a", "B"]] 235x[[1, 2]] 236x[[1, "B"]] 237})%> 238``` 239We can assign individual elements similarly, e.g. 240```r 241<%=withCapture({ 242x[["b", "B"]] <- -x[["b", "B"]] 243as.list(x) 244})%> 245``` 246We can also assign multiple elements through dimensional subsetting, e.g. 247```r 248<%=withCapture({ 249x[2, -1] <- 98:99 250as.list(x) 251x["a", c(1, 3)] <- list(97, "foo") 252as.list(x) 253x[] <- 1:6 254as.list(x) 255})%> 256``` 257 258Concurrently with dimensional names it is possible to have names of the individual elements just as for list environments without dimensions. For example, 259```r 260<%=withCapture({ 261names(x) <- letters[seq_along(x)] 262x 263x[["a"]] 264x[["f"]] 265x[c("a", "f")] 266unlist(x) 267as.list(x) 268})%> 269``` 270Contrary to lists, element names are preserved also with multi-dimensional subsetting, e.g. 271```r 272<%=withCapture({ 273x[1, 2] 274x[1, 2, drop = FALSE] 275x[1:2, 2:1] 276x[2, ] 277x[2, , drop = FALSE] 278x["b", -2, drop = FALSE] 279})%> 280``` 281 282 283Note, whenever dimensions are set using `dim(x) <- dims` both the dimensional names and the element names are removed, e.g. 284```r 285> dim(x) <- NULL 286> names(x) 287NULL 288``` 289This behavior is by design, cf. `help("dim", package="base")`. 290 291 292To allocate an "empty" list environment array (with all `NULL`:s) of a given dimension, do 293```r 294<%=withCapture({ 295x <- listenv() 296dim(x) <- c(2, 3) 297dimnames(x) <- list(c("a", "b"), c("A", "B", "C")) 298x 299})%> 300``` 301Rows and columns can be dropped by assigning `NULL`, e.g. to drop the first and third column of a list-environment matrix, do: 302```r 303<%=withCapture({ 304x[, c(1, 3)] <- NULL 305x 306})%> 307``` 308 309 310<%--- 311Because of this, the listenv package provides the `undim()` function, which removes the dimensions but preserves the names, e.g. 312```r 313<%=withCapture({ 314x <- undim(x) 315names(x) 316})%> 317``` 318_Warning_: Since list environments _and their attributes_ are mutable, calling 319```r 320undim(x) 321``` 322will have the same effect as 323```r 324x <- undim(x) 325``` 326That is, the dimension attributes of `x` will be changed. The reason for this is explained in Section 'Important about environments' above. 327---%> 328 329 330## Important about environments 331List environments are as their name suggests _environments_. Whenever working with environments in R, it is important to understand that _environments are mutable_ whereas all other of the basic data types in R are immutable. For example, consider the following function that assigns zero to element `a` of object `x`: 332```r 333<%=withCapture({ 334setA <- function(x) { 335 x$a <- 0 336 x 337} 338})%> 339``` 340If we pass a regular list to this function, 341```r 342<%=withCapture({ 343x <- list(a = 1) 344y <- setA(x) 345x$a 346y$a 347})%> 348``` 349we see that `x` is unaffected by the assignment. This is because _lists are immutable_ in R. However, if we pass an environment instead, 350```r 351<%=withCapture({ 352x <- new.env() 353x$a <- 1 354y <- setA(x) 355x$a 356y$a 357})%> 358``` 359we find that `x` was affected by the assignment. This is because _environments are mutable_ in R. Since list environments inherits from environments, this also goes for them, e.g. 360```r 361<%=withCapture({ 362x <- listenv(a = 1) 363y <- setA(x) 364x$a 365y$a 366})%> 367``` 368 369What is also important to understand is that it is not just the _content_ of an environment that is mutable but also its _attributes_. For example, 370```r 371<%=withCapture({ 372x <- listenv(a = 1) 373y <- x 374attr(y, "foo") <- "Hello!" 375attr(x, "foo") 376})%> 377``` 378More importantly, since dimensions and their names are also attributes, this also means they are mutable. For example, 379```r 380<%=withCapture({ 381x <- as.listenv(1:6) 382dim(x) <- c(2, 3) 383x 384y <- x 385dim(y) <- c(3, 2) 386x 387})%> 388``` 389 390 391[listenv]: https://cran.r-project.org/package=listenv 392 393--- 394Copyright Henrik Bengtsson, 2015-2018 395