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:
Property | list environments | lists | environments |
---|---|---|---|
Number of elements, e.g. length()
|
yes | yes | yes |
Named elements, e.g. names() , x$a and
x[["a"]]
|
yes | yes | yes |
Duplicated names | yes | yes | |
Element names are optional | yes | yes | |
Indexed elements, e.g. x[[4]]
|
yes | yes | |
Dimensions, e.g. dim(x) , t(x) , and
aperm(x, c(3,1,2))
|
yes | yes | |
Names of dimensions, e.g. dimnames(x)
|
yes | yes | |
Indexing by dimensions, e.g. x[[2, 4]] and
x[[2, "D"]]
|
yes | yes | |
Multi-element subsetting, e.g. x[c("a", "c")] ,
x[-1] and x[2:1, , 3]
|
yes | yes | |
Multi-element subsetting preserves element names | yes | ||
Resizing, e.g. length(x) <- 6
|
yes | yes | |
Removing elements by assigning NULL,
e.g. x$c <- NULL and x[1:3] <- NULL
|
yes | yes | |
Removing parts of dimensions by assigning NULL,
e.g. x[,2] <- NULL
|
yes | ||
Mutable,
e.g. y <- x; y$a <- 3; identical(y, x)
|
yes | yes | |
Compatible* with assign() ,
delayedAssign() , get() and
exists()
|
yes | yes |
For example,
> x <- listenv(a = 1, b = 2, c = "hello")
> x
A ‘listenv’ vector with 3 elements (‘a’, ‘b’, ‘c’).
> length(x)
[1] 3
> names(x)
[1] "a" "b" "c"
> x$a
[1] 1
> x[[3]] <- toupper(x[[3]])
> x$c
[1] "HELLO"
> y <- x
> y$d <- y$a + y[["b"]]
> names(y)[2] <- "a"
> y$a
[1] 1
> y
A ‘listenv’ vector with 4 elements (‘a’, ‘a’, ‘c’, ‘d’).
> identical(y, x)
[1] TRUE
> for (ii in seq_along(x)) {
+ cat(sprintf("Element %d (%s): %s\n", ii, sQuote(names(x)[ii]),
+ x[[ii]]))
+ }
Element 1 (‘a’): 1
Element 2 (‘a’): 2
Element 3 (‘c’): HELLO
Element 4 (‘d’): 3
> x[c(1, 3)] <- list(2, "Hello world!")
> x
A ‘listenv’ vector with 4 elements (‘a’, ‘a’, ‘c’, ‘d’).
> y <- as.list(x)
> str(y)
List of 4
$ a: num 2
$ a: num 2
$ c: chr "Hello world!"
$ d: num 3
> z <- as.listenv(y)
> z
A ‘listenv’ vector with 4 elements (‘a’, ‘a’, ‘c’, ‘d’).
> identical(z, x)
[1] FALSE
> all.equal(z, x)
[1] TRUE
List environments are created similarly to lists but also similarly to environments. To create an empty list environment, use
This can later can be populated using named assignments,
comparable to how both lists and environments work. Similarly to lists, they can also be populated using indices, e.g.
Just 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.
As with lists, the above list environment can also be created from the start, e.g.
> x <- listenv(a = 1, 3, c = 4, NULL, 5)
> x
A ‘listenv’ vector with 5 elements (‘a’, ‘’, ‘c’, ‘’, ‘’).
As for lists, the length of a list environment can at any time be increased or decreased by assigning it a new length. If decreased, elements are dropped, e.g.
> x
A ‘listenv’ vector with 5 elements (‘a’, ‘’, ‘c’, ‘’, ‘’).
> length(x) <- 2
> x
A ‘listenv’ vector with 2 elements (‘a’, ‘’).
> x[[1]]
[1] 1
> x[[2]]
[1] 3
If increased, new elements are populated with unnamed elements of
NULL
, e.g.
> length(x) <- 4
> x
A ‘listenv’ vector with 4 elements (‘a’, ‘’, ‘’, ‘’).
> x[[3]]
NULL
> x[[4]]
NULL
To allocate an “empty” list environment (with all
NULL
:s) of a given length, do
Note: Unfortunately, it is not possible to use
x <- vector("listenv", length = 4)
; that construct is
only supported for the basic data types.
Elements can be dropped by assigning NULL
, e.g. to drop
the first and third element of a list environment, do:
Analogously to lists and plain environments, it is possible to iterate over elements of list environments by the element names. For example,
Coercing a list environment to a list:
> x <- listenv(a = 2, b = 3, c = "hello")
> x
A ‘listenv’ vector with 3 elements (‘a’, ‘b’, ‘c’).
> y <- as.list(x)
> str(y)
List of 3
$ a: num 2
$ b: num 3
$ c: chr "hello"
Coercing a list to a list environment:
> z <- as.listenv(y)
> z
A ‘listenv’ vector with 3 elements (‘a’, ‘b’, ‘c’).
> identical(z, x)
[1] FALSE
> all.equal(z, x)
[1] TRUE
Unlisting:
Analogously to lists, and contrary to plain environments, list environments can have dimensions with corresponding names. For example,
> x <- as.listenv(1:6)
> dim(x) <- c(2, 3)
> dimnames(x) <- list(c("a", "b"), c("A", "B", "C"))
> x
A ‘listenv’ matrix with 6 elements (unnamed) arranged in 2x3 rows (‘a’, ‘b’) and columns (‘A’, ‘B’, ‘C’).
An easy way to quickly get an overview is to coerce to a list, e.g.
Individual elements of a list environment can be accessed using standard subsetting syntax, e.g.
We can assign individual elements similarly, e.g.
We can also assign multiple elements through dimensional subsetting, e.g.
> x[2, -1] <- 98:99
> as.list(x)
A B C
a 1 3 5
b 2 98 99
> x["a", c(1, 3)] <- list(97, "foo")
> as.list(x)
A B C
a 97 3 "foo"
b 2 98 99
> x[] <- 1:6
> as.list(x)
A B C
a 1 3 5
b 2 4 6
Concurrently with dimensional names it is possible to have names of the individual elements just as for list environments without dimensions. For example,
> names(x) <- letters[seq_along(x)]
> x
A ‘listenv’ matrix with 6 elements (‘a’, ‘b’, ‘c’, ..., ‘f’) arranged in 2x3 rows (‘a’, ‘b’) and columns (‘A’, ‘B’, ‘C’).
> x[["a"]]
[1] 1
> x[["f"]]
[1] 6
> x[c("a", "f")]
A ‘listenv’ vector with 2 elements (‘a’, ‘f’).
> unlist(x)
a b c d e f
1 2 3 4 5 6
> as.list(x)
A B C
a 1 3 5
b 2 4 6
attr(,"names")
[1] "a" "b" "c" "d" "e" "f"
Contrary to lists, element names are preserved also with multi-dimensional subsetting, e.g.
> x[1, 2]
A ‘listenv’ vector with 1 element (‘c’).
> x[1, 2, drop = FALSE]
A ‘listenv’ matrix with 1 element (‘c’) arranged in 1x1 rows (‘a’) and columns (‘B’).
> x[1:2, 2:1]
A ‘listenv’ matrix with 4 elements (‘c’, ‘d’, ‘a’, ‘b’) arranged in 2x2 rows (‘a’, ‘b’) and columns (‘B’, ‘A’).
> x[2, ]
A ‘listenv’ vector with 3 elements (‘b’, ‘d’, ‘f’).
> x[2, , drop = FALSE]
A ‘listenv’ matrix with 3 elements (‘b’, ‘d’, ‘f’) arranged in 1x3 rows (‘b’) and columns (‘A’, ‘B’, ‘C’).
> x["b", -2, drop = FALSE]
A ‘listenv’ matrix with 2 elements (‘b’, ‘f’) arranged in 1x2 rows (‘b’) and columns (‘A’, ‘C’).
Note, whenever dimensions are set using
dim(x) <- dims
both the dimensional names and the
element names are removed, e.g.
This behavior is by design,
cf. help("dim", package="base")
.
To allocate an “empty” list environment array (with all
NULL
:s) of a given dimension, do
> x <- listenv()
> dim(x) <- c(2, 3)
> dimnames(x) <- list(c("a", "b"), c("A", "B", "C"))
> x
A ‘listenv’ matrix with 6 elements (unnamed) arranged in 2x3 rows (‘a’, ‘b’) and columns (‘A’, ‘B’, ‘C’).
Rows and columns can be dropped by assigning NULL
,
e.g. to drop the first and third column of a list-environment matrix,
do:
List 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
:
If we pass a regular list to this function,
we see that x
is unaffected by the assignment. This is
because lists are immutable in R. However, if we pass an
environment instead,
we 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.
What 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,
More importantly, since dimensions and their names are also attributes, this also means they are mutable. For example,
> x <- as.listenv(1:6)
> dim(x) <- c(2, 3)
> x
A ‘listenv’ matrix with 6 elements (unnamed) arranged in 2x3 unnamed rows and columns.
> y <- x
> dim(y) <- c(3, 2)
> x
A ‘listenv’ matrix with 6 elements (unnamed) arranged in 3x2 unnamed rows and columns.
Copyright Henrik Bengtsson, 2015-2018