The refer
package
allows users to keep references to objects and modify objects in place
with relying on reference classes. This article describes how to use
ref
objects by moving objects around a map. Please note
that many of the operations in the refer
package go against
the philosophy of R and may lead to inconsistent and unclear code; use
sparingly and with caution.
First, we need to load the refer
package.
Our goal with this project is to population a map. We will use a character matrix and keep it small at 10x10.
map <- matrix(' ', nrow=10, ncol=10)
map
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#> [1,] " " " " " " " " " " " " " " " " " " " "
#> [2,] " " " " " " " " " " " " " " " " " " " "
#> [3,] " " " " " " " " " " " " " " " " " " " "
#> [4,] " " " " " " " " " " " " " " " " " " " "
#> [5,] " " " " " " " " " " " " " " " " " " " "
#> [6,] " " " " " " " " " " " " " " " " " " " "
#> [7,] " " " " " " " " " " " " " " " " " " " "
#> [8,] " " " " " " " " " " " " " " " " " " " "
#> [9,] " " " " " " " " " " " " " " " " " " " "
#> [10,] " " " " " " " " " " " " " " " " " " " "
Next, let us create a person to place on the map. The person will keep track of its location and a reference to the map object. We will place a representative of the person on the map.
Since a reference of the map is placed inside 'person'
,
we can always use it to indirectly access the map. Just calling
person$map
, though, only returns the reference not the
actual item.
To return the underlying object, we must ‘dereference’ the item using
either the deref()
function or the !
operator.
!person$map
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#> [1,] "X" " " " " " " " " " " " " " " " " " "
#> [2,] " " " " " " " " " " " " " " " " " " " "
#> [3,] " " " " " " " " " " " " " " " " " " " "
#> [4,] " " " " " " " " " " " " " " " " " " " "
#> [5,] " " " " " " " " " " " " " " " " " " " "
#> [6,] " " " " " " " " " " " " " " " " " " " "
#> [7,] " " " " " " " " " " " " " " " " " " " "
#> [8,] " " " " " " " " " " " " " " " " " " " "
#> [9,] " " " " " " " " " " " " " " " " " " " "
#> [10,] " " " " " " " " " " " " " " " " " " " "
If we add a new object to the original map, then the change is
reflected when it is dereferenced from person
.
map[1,5] <- "O"
!person$map
#> [,1] [,2] [,3] [,4] [,5] [,6] [,7] [,8] [,9] [,10]
#> [1,] "X" " " " " " " "O" " " " " " " " " " "
#> [2,] " " " " " " " " " " " " " " " " " " " "
#> [3,] " " " " " " " " " " " " " " " " " " " "
#> [4,] " " " " " " " " " " " " " " " " " " " "
#> [5,] " " " " " " " " " " " " " " " " " " " "
#> [6,] " " " " " " " " " " " " " " " " " " " "
#> [7,] " " " " " " " " " " " " " " " " " " " "
#> [8,] " " " " " " " " " " " " " " " " " " " "
#> [9,] " " " " " " " " " " " " " " " " " " " "
#> [10,] " " " " " " " " " " " " " " " " " " " "
ref
can also be used to build expressions that contain
references. For example, location
below contains a
reference to the row
and col
of
person
.Dereferencing location
evaluates the
expression, taking note of where person is located when originally
created. The effect is similar to creating an active binding. However,
active bindings are heavier and much more difficult to pass around,
inspect, and so forth.
Since location
is a reference, updating either
row
or col
will change the dereferenced value
of location.
Note that ref
objects automatically dereference when
applied to many base functions such arithmetic operators. This includes
the standard extraction operators: $
, [
, and
[[
. However, these do not overwrite the underlying
data.
A slice
is a special type of reference that refers to
part of an object. For example, we could create a slice that points to
the second row and last 5 columns of the map. If these values change,
the slice reflects these changes.
When dereferenced, the above slice
calls
map[2, 6:10]
within the environment that map is located.
Since ref
objects automatically dereference when extraction
calls are made, slice
could even be used on another
reference.
The ref
package contains another of functions to modify
objects in place. For example, it includes variations on the standard
+=
and -=
operators found in many languages
such as Python.
These functions can also accept other reference objects. When a reference object is used, the underlying object is modified. This can be dangerous, so use sparingly!
Objects can also be modified in place with custom functions using
modify_by
.
modify_by(x, sqrt)
x
#> [1] 1.000000 1.414214 3.605551 3.741657 3.872983 4.000000 2.645751 2.828427
#> [9] 3.000000 3.162278
modify_by
can also be used to completely overwrite the
value of an object further up the search path by passing a value rather
than a function.
The general ref
function automatically dereferences when
passed to a wide variety of functions and can modify the underlying
objects in place. sref
is an alternative version of
ref
that does away with this behavior. sref
objects can still be dereferenced as normal, but attempts to modify or
apply functions to the reference will throw an error. Use
sslice
to create sref
versions of slices.