From 70093bf4d3ba720b093f62d60ee9d06ed600e99c Mon Sep 17 00:00:00 2001
From: "Pavel N. Krivitsky"
Date: Mon, 18 Nov 2024 14:31:44 +1100
Subject: [PATCH] Added a function, modify_in_place(), that attempts to modify
the argument of its caller in place; bumped the released version.
---
DESCRIPTION | 6 ++--
NAMESPACE | 1 +
R/misc.utilities.R | 65 ++++++++++++++++++++++++++++++++++++++++++
man/modify_in_place.Rd | 63 ++++++++++++++++++++++++++++++++++++++++
4 files changed, 132 insertions(+), 3 deletions(-)
create mode 100644 man/modify_in_place.Rd
diff --git a/DESCRIPTION b/DESCRIPTION
index d8dee4d..4e94d15 100644
--- a/DESCRIPTION
+++ b/DESCRIPTION
@@ -1,6 +1,6 @@
Package: statnet.common
-Version: 4.10.0-447
-Date: 2024-09-23
+Version: 4.11.0-453
+Date: 2024-11-18
Title: Common R Scripts and Utilities Used by the Statnet Project Software
Authors@R: c(
person(c("Pavel", "N."), "Krivitsky", role=c("aut","cre"), email="pavel@statnet.org", comment=c(ORCID="0000-0002-9101-3362", affiliation="University of New South Wales")),
@@ -13,7 +13,7 @@ BugReports: https://github.com/statnet/statnet.common/issues
License: GPL-3 + file LICENSE
URL: https://statnet.org
Roxygen: list(markdown = TRUE)
-RoxygenNote: 7.3.2
+RoxygenNote: 7.3.2.9000
Encoding: UTF-8
Suggests: covr,
rlang (>= 1.1.1),
diff --git a/NAMESPACE b/NAMESPACE
index 0a92d52..5b81c90 100644
--- a/NAMESPACE
+++ b/NAMESPACE
@@ -106,6 +106,7 @@ export(lweighted.cov)
export(lweighted.mean)
export(lweighted.var)
export(message_print)
+export(modify_in_place)
export(nonsimp.update.formula)
export(nonsimp_update.formula)
export(once)
diff --git a/R/misc.utilities.R b/R/misc.utilities.R
index 49165ff..39d0191 100644
--- a/R/misc.utilities.R
+++ b/R/misc.utilities.R
@@ -926,3 +926,68 @@ unused_dots_warning <- function(e){
rlang::warn(sprintf("Argument(s) %s were not recognized or used. Did you mistype an argument name?",
paste.and(sQuote(v))))
}
+
+#' Modify the argument in the calling environment of the calling function
+#'
+#' This is a helper function that enables a function to modify its argument in place, emulating behavior of \CRANpkg{R6} classes and methods in the \CRANpkg{network}. It should typically be the last line of the calling function.
+#'
+#' This function determines whether the argument can be assigned to by actually attempting to do so. If this results in an error, for example, because the argument is anonymous, the error is silently ignored.
+#'
+#' It can be called multiple times by the same function to modify multiple arguments. It uses the [on.exit()] mechanism, adding to the list. Thus, if some other function calls `on.exit(..., add = FALSE)` (the default) afterwards, `modify_in_place()` will fail silently.
+#'
+#' @param x the argument (not its name!) to be modified
+#' @param value the value to assign (defaulting to the current value of `x`)
+#'
+#' @return `value`, invisibly, while attempting to modify `x` in place
+#'
+#' @examples
+#' ## A function that increments its argument in place:
+#' inc <- function(x){
+#' modify_in_place(x, x+1)
+#' }
+#'
+#' y <- 1
+#' z <- 1
+#'
+#' stopifnot(inc(z) == 2)
+#' stopifnot(z == 2)
+#' stopifnot(inc(y) == 2)
+#' stopifnot(y == 2)
+#' stopifnot(inc(z) == 3)
+#' stopifnot(z == 3)
+#'
+#' stopifnot(inc(identity(z)) == 4)
+#' stopifnot(z == 3) # Not updated!
+#'
+#' ## Modify an argument that's been updated in place:
+#' inc2 <- function(y){
+#' y <- y + 1
+#' modify_in_place(y)
+#' }
+#'
+#' z
+#' stopifnot(inc2(z) == 4)
+#' stopifnot(z == 4)
+#'
+#' ## Decrement the first argument, increment the second:
+#' incdec <- function(x,y){
+#' modify_in_place(x, x-1)
+#' modify_in_place(y, y+1)
+#' }
+#'
+#' c(y,z)
+#' incdec(y,z)
+#' stopifnot(all(c(y,z) == c(1,5)))
+#' @export
+modify_in_place <- function(x, value = x){
+ xn <- substitute(x) # Grab the name of the argument to be updated.
+ xnn <- match.call(sys.function(-1), sys.call(-1))[[xn]] # Grab the expression that was passed into its argument.
+
+ eval.parent(on.exit( # As the calling function exits...
+ tryCatch( # try to...
+ eval.parent(call("<-", xnn, value), n = 2), # Assign `value` to whatever `xnn` stands for in the caller's calling environment.
+ error = identity # and do nothing if it fails.
+ ), add = TRUE))
+
+ invisible(value) # Return invisibly.
+}
diff --git a/man/modify_in_place.Rd b/man/modify_in_place.Rd
new file mode 100644
index 0000000..c11438d
--- /dev/null
+++ b/man/modify_in_place.Rd
@@ -0,0 +1,63 @@
+% Generated by roxygen2: do not edit by hand
+% Please edit documentation in R/misc.utilities.R
+\name{modify_in_place}
+\alias{modify_in_place}
+\title{Modify the argument in the calling environment of the calling function}
+\usage{
+modify_in_place(x, value = x)
+}
+\arguments{
+\item{x}{the argument (not its name!) to be modified}
+
+\item{value}{the value to assign (defaulting to the current value of \code{x})}
+}
+\value{
+\code{value}, invisibly, while attempting to modify \code{x} in place
+}
+\description{
+This is a helper function that enables a function to modify its argument in place, emulating behavior of \CRANpkg{R6} classes and methods in the \CRANpkg{network}. It should typically be the last line of the calling function.
+}
+\details{
+This function determines whether the argument can be assigned to by actually attempting to do so. If this results in an error, for example, because the argument is anonymous, the error is silently ignored.
+
+It can be called multiple times by the same function to modify multiple arguments. It uses the \code{\link[=on.exit]{on.exit()}} mechanism, adding to the list. Thus, if some other function calls \code{on.exit(..., add = FALSE)} (the default) afterwards, \code{modify_in_place()} will fail silently.
+}
+\examples{
+## A function that increments its argument in place:
+inc <- function(x){
+ modify_in_place(x, x+1)
+}
+
+y <- 1
+z <- 1
+
+stopifnot(inc(z) == 2)
+stopifnot(z == 2)
+stopifnot(inc(y) == 2)
+stopifnot(y == 2)
+stopifnot(inc(z) == 3)
+stopifnot(z == 3)
+
+stopifnot(inc(identity(z)) == 4)
+stopifnot(z == 3) # Not updated!
+
+## Modify an argument that's been updated in place:
+inc2 <- function(y){
+ y <- y + 1
+ modify_in_place(y)
+}
+
+z
+stopifnot(inc2(z) == 4)
+stopifnot(z == 4)
+
+## Decrement the first argument, increment the second:
+incdec <- function(x,y){
+ modify_in_place(x, x-1)
+ modify_in_place(y, y+1)
+}
+
+c(y,z)
+incdec(y,z)
+stopifnot(all(c(y,z) == c(1,5)))
+}