Skip to content

Commit 0a9f1ac

Browse files
committed
Added envir(), envir<-(), and sign<-() generics and implemented their methods for term_list objects, encapsulating their implementation.
1 parent 2a7e209 commit 0a9f1ac

7 files changed

Lines changed: 194 additions & 54 deletions

File tree

DESCRIPTION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Package: statnet.common
2-
Version: 4.13.0-503
3-
Date: 2025-10-09
2+
Version: 4.13.0-504
3+
Date: 2025-11-28
44
Title: Common R Scripts and Utilities Used by the Statnet Project Software
55
Authors@R: c(
66
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")),

NAMESPACE

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,15 @@ S3method("$",control.list)
44
S3method("[",term_list)
55
S3method("[",wmatrix)
66
S3method("[<-",wmatrix)
7+
S3method("envir<-",default)
8+
S3method("envir<-",term_list)
79
S3method("lrowweights<-",linwmatrix)
810
S3method("lrowweights<-",logwmatrix)
911
S3method("lrowweights<-",matrix)
1012
S3method("rowweights<-",linwmatrix)
1113
S3method("rowweights<-",logwmatrix)
1214
S3method("rowweights<-",matrix)
15+
S3method("sign<-",term_list)
1316
S3method(as.control.list,control.list)
1417
S3method(as.control.list,list)
1518
S3method(as.linwmatrix,linwmatrix)
@@ -27,6 +30,8 @@ S3method(compress_rows,logwmatrix)
2730
S3method(decompress_rows,compressed_rows_df)
2831
S3method(decompress_rows,wmatrix)
2932
S3method(diff,control.list)
33+
S3method(envir,default)
34+
S3method(envir,term_list)
3035
S3method(lrowweights,linwmatrix)
3136
S3method(lrowweights,logwmatrix)
3237
S3method(order,data.frame)
@@ -40,6 +45,7 @@ S3method(print,term_list)
4045
S3method(print,wmatrix)
4146
S3method(rowweights,linwmatrix)
4247
S3method(rowweights,logwmatrix)
48+
S3method(sign,term_list)
4349
S3method(sort,data.frame)
4450
S3method(split,array)
4551
S3method(split,matrix)
@@ -48,9 +54,11 @@ S3method(trim_env,environment)
4854
S3method(update,Welford)
4955
export("EVL<-")
5056
export("NVL<-")
57+
export("envir<-")
5158
export("lrowweights<-")
5259
export("replace<-")
5360
export("rowweights<-")
61+
export("sign<-")
5462
export("ult<-")
5563
export(.Deprecate_method)
5664
export(.Deprecate_once)
@@ -88,6 +96,7 @@ export(default_options)
8896
export(despace)
8997
export(empty_env)
9098
export(enlist)
99+
export(envir)
91100
export(eval_lhs.formula)
92101
export(filter_rhs.formula)
93102
export(fixed.pval)

R/formula.utilities.R

Lines changed: 88 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,11 @@ NULL
2828
#' @param object formula object to be updated or evaluated
2929
#' @param newterms a [`term_list`] object, or any list of terms (names
3030
#' or calls) to append to the formula, or a formula whose RHS terms
31-
#' will be used; its `"sign"` attribute vector can give the sign of
32-
#' each term (`+1` or `-1`), and its `"env"` attribute
33-
#' vector will be used to set its environment, with the first
34-
#' available being used and subsequent ones producing a warning.
31+
#' will be used; it can have a [sign()] method or a `"sign"`
32+
#' attribute vector can give the sign of each term (`+1` or `-1`),
33+
#' and its [envir()] method or `"env"` attribute vector will be used
34+
#' to set its environment, with the first available being used and
35+
#' subsequent ones producing a warning.
3536
#' @param keep.onesided if the initial formula is one-sided, keep it
3637
#' whether to keep it one-sided or whether to make the initial
3738
#' formula the new LHS
@@ -83,15 +84,15 @@ append_rhs.formula <- function(object = NULL, newterms, keep.onesided = FALSE, e
8384

8485
for(i in seq_along(newterms)){
8586
newterm <- newterms[[i]]
86-
termsign <- if(NVL(attr(newterms, "sign")[i], +1)>0) "+" else "-"
87+
termsign <- if(NVL(ERRVL2(sign(newterms)[i], NULL), attr(newterms, "sign")[i], +1)>0) "+" else "-"
8788
if(length(object)==0){
8889
if(termsign == "-") newterm <- call(termsign, newterm)
8990
object <- as.formula(call("~", newterm))
9091
}else if(length(object)==3) object[[3L]]<-call(termsign,object[[3L]],newterm)
9192
else if(keep.onesided) object[[2L]]<-call(termsign,object[[2L]],newterm)
9293
else object[[3L]]<- if(termsign=="+") newterm else call(termsign,newterm)
9394

94-
NVL(env) <- termenv <- attr(newterms, "env")[[i]]
95+
NVL(env) <- termenv <- NVL(ERRVL2(envir(newterms), NULL), attr(newterms, "env"))[[i]]
9596
if(!is.null(termenv) && !identical(env, termenv))
9697
warning(sQuote(paste0("newterms[[",i,"]]")), " has an environment that differs from the specified environment or another term's environment.")
9798
}
@@ -262,15 +263,16 @@ nonsimp.update.formula<-function (object, new, ..., from.new=FALSE){
262263
#' A helper class for list of terms in an formula
263264
#'
264265
#' Typically generated by [list_rhs.formula()], it contains, in
265-
#' addition to a list of [call()] or similar objects, attributes
266-
#' `"sign"` and `"env"`, containing, respectively a vector of
267-
#' signs that the terms had in the original formula and a list of
268-
#' environments of the formula from which the term has been
269-
#' extracted. Indexing and concatenation methods preserve these.
266+
#' addition to a list of [call()] or similar objects information about
267+
#' the sign of the term and the environment of the formula from which
268+
#' the term has been extracted, accessible and modifiable via [sign()]
269+
#' and [envir()] generics. Indexing and concatenation methods preserve
270+
#' these.
270271
#'
271-
#' @param x a list of terms or a term; a `term_list`
272+
#' @param x,object a list of terms or a term; a `term_list`
272273
#' @param sign a vector specifying the signs associated with each term (`-1` and `+1`)
273274
#' @param env a list specifying the environments, or NULL
275+
#' @param value RHS; see method documentation
274276
#' @param i list index
275277
#' @param ... additional arguments to methods
276278
#'
@@ -289,23 +291,24 @@ nonsimp.update.formula<-function (object, new, ..., from.new=FALSE){
289291
#' (l <- c(l1,l2))
290292
#' \dontshow{
291293
#' stopifnot(identical(c(unclass(l)), alist(b, c, NULL, 1)))
292-
#' stopifnot(identical(attr(l, "sign"), c(1,1,-1,1)))
293-
#' stopifnot(identical(attr(l, "env"), rep(list(e1, globalenv()), each=2)))
294+
#' stopifnot(identical(sign(l), c(1L,1L,-1L,1L)))
295+
#' stopifnot(identical(envir(l), rep(list(e1, globalenv()), each=2)))
294296
#' }
295297
#'
296298
#' (l <- c(l2[1], l1[2], l1[1], l1[1], l2[2]))
299+
#' sign(l)[3] <- -1L
297300
#' \dontshow{
298301
#' stopifnot(identical(c(unclass(l)), alist(NULL, c, b, b, 1)))
299-
#' stopifnot(identical(attr(l, "sign"), c(-1,1,1,1,1)))
300-
#' stopifnot(identical(attr(l, "env"), list(globalenv(), e1, e1, e1, globalenv())))
302+
#' stopifnot(identical(sign(l), c(-1L,1L,-1L,1L,1L)))
303+
#' stopifnot(identical(envir(l), list(globalenv(), e1, e1, e1, globalenv())))
301304
#' }
302305
#'
303306
#' @export
304-
term_list <- function(x, sign = +1, env = NULL){
307+
term_list <- function(x, sign = +1L, env = NULL){
305308
if(!is.list(x)) x <- list(x)
306309
if(!is.list(env)) env <- list(env)
307310
structure(x,
308-
sign = rep(sign, length.out=length(x)),
311+
sign = rep(as.integer(sign), length.out=length(x)),
309312
env = rep(env, length.out=length(x)),
310313
class = "term_list")
311314
}
@@ -320,38 +323,93 @@ as.term_list.term_list <- function(x, ...) x
320323

321324
#' @rdname term_list
322325
#' @export
323-
as.term_list.default <- function(x, sign = +1, env = NULL, ...) term_list(x, sign=sign, env=env)
326+
as.term_list.default <- function(x, sign = +1L, env = NULL, ...) term_list(x, sign=sign, env=env)
324327

325328
#' @rdname term_list
326329
#' @export
327330
c.term_list <- function(x, ...){
328331
xl <- c(list(as.term_list(x)), lapply(list(...), as.term_list))
329332
structure(
330333
c(unclass(x), ...),
331-
sign = unlist(lapply(xl, attr, "sign"), use.names=FALSE),
332-
env = unlist(lapply(xl, attr, "env"), recursive=FALSE, use.names=FALSE),
334+
sign = unlist(lapply(xl, sign), use.names = FALSE),
335+
env = unlist(lapply(xl, envir), recursive = FALSE, use.names = FALSE),
333336
class = "term_list"
334337
)
335338
}
336339

337340
#' @rdname term_list
338341
#' @export
339342
`[.term_list` <- function(x, i, ...){
340-
term_list(NextMethod(), sign = attr(x, "sign")[i], env = attr(x, "env")[i])
343+
term_list(NextMethod(), sign = sign(x)[i], env = envir(x)[i])
341344
}
342345

343346
#' @rdname term_list
344347
#' @export
345348
print.term_list <- function(x, ...){
346-
signstr <- ifelse(attr(x, "sign")>=0, "+", "-")
347-
envstr <- sapply(attr(x, "env"), format)
349+
signstr <- ifelse(sign(x) >= 0L, "+", "-")
350+
envstr <- sapply(envir(x), format)
348351
termstr <- lapply(lapply(x, format), paste0, collapse="\n")
349352

350353
cat("Term List:\n")
351354
cat(paste(signstr, termstr, envstr, collapse="\n"))
352355
cat("\n")
353356
}
354357

358+
#' @describeIn term_list An [`integer`] vector giving the signs of
359+
#' each term in the list.
360+
#' @export
361+
sign.term_list <- function(x) attr(x, "sign")
362+
363+
#' A generic for setting the sign of an object
364+
#'
365+
#' @param x object whose sign is to be set
366+
#' @param value a numeric vector specifying the sign
367+
#' @export
368+
`sign<-` <- function(x, value) UseMethod("sign<-")
369+
370+
#' @describeIn term_list Update the signs of the terms; `value` is
371+
#' recycled to the length of the list.
372+
#' @export
373+
`sign<-.term_list` <- function(x, value) structure(x, sign = rep_len(as.integer(sign(value)), length(x)))
374+
375+
#' A generic for querying and setting an object's environment
376+
#'
377+
#' [environment()] and [environment<-()] are not generics, so it is
378+
#' not possible to dispatch based on the class of the object affected.
379+
#'
380+
#' When no method is available, these generics fall back to the
381+
#' [environment()] and [environment<-()] functions.
382+
#'
383+
#' @param object object whose environment is to be queried or set
384+
#' @param value typically an [`environment`], but could be any RHS
385+
#' supported by the method
386+
#'
387+
#' @export
388+
`envir` <- function(object) UseMethod("envir")
389+
390+
#' @noRd
391+
#' @export
392+
`envir.default` <- function(object) environment(object)
393+
394+
#' @rdname envir
395+
#' @export
396+
`envir<-` <- function(object, value) UseMethod("envir<-")
397+
398+
#' @noRd
399+
#' @export
400+
`envir<-.default` <- function(object, value) `environment<-`(object, value)
401+
402+
#' @describeIn term_list A [`list`] with an element for each term in
403+
#' the list, giving its environment.
404+
#' @export
405+
`envir.term_list` <- function(object) attr(object, "env")
406+
407+
#' @describeIn term_list Update the environments of the terms; `value`
408+
#' can be an environment or a list of environments, recycled to the
409+
#' length of the term list.
410+
#' @export
411+
`envir<-.term_list` <- function(object, value) structure(object, env = rep_len(enlist(value), length(object)))
412+
355413
.recurse_summation <- function(x, sign){
356414
if(length(x)==1)
357415
term_list(x, sign)
@@ -414,21 +472,21 @@ list_summands.call<-function(object){
414472
#'
415473
#' @examples
416474
#' stopifnot(identical(list_rhs.formula(a~b),
417-
#' structure(alist(b), sign=1, env=list(globalenv()), class="term_list")))
475+
#' structure(alist(b), sign=1L, env=list(globalenv()), class="term_list")))
418476
#' stopifnot(identical(list_rhs.formula(~b),
419-
#' structure(alist(b), sign=1, env=list(globalenv()), class="term_list")))
477+
#' structure(alist(b), sign=1L, env=list(globalenv()), class="term_list")))
420478
#' stopifnot(identical(list_rhs.formula(~b+NULL),
421479
#' structure(alist(b, NULL),
422-
#' sign=c(1,1), env=rep(list(globalenv()), 2), class="term_list")))
480+
#' sign=c(1L,1L), env=rep(list(globalenv()), 2), class="term_list")))
423481
#' stopifnot(identical(list_rhs.formula(~-b+NULL),
424482
#' structure(alist(b, NULL),
425-
#' sign=c(-1,1), env=rep(list(globalenv()), 2), class="term_list")))
483+
#' sign=c(-1L,1L), env=rep(list(globalenv()), 2), class="term_list")))
426484
#' stopifnot(identical(list_rhs.formula(~+b-NULL),
427485
#' structure(alist(b, NULL),
428-
#' sign=c(1,-1), env=rep(list(globalenv()), 2), class="term_list")))
486+
#' sign=c(1L,-1L), env=rep(list(globalenv()), 2), class="term_list")))
429487
#' stopifnot(identical(list_rhs.formula(~+b-(NULL+c)),
430488
#' structure(alist(b, NULL, c),
431-
#' sign=c(1,-1,-1), env=rep(list(globalenv()), 3), class="term_list")))
489+
#' sign=c(1L,-1L,-1L), env=rep(list(globalenv()), 3), class="term_list")))
432490
#'
433491
#' @export
434492
list_rhs.formula<-function(object){

man/envir.Rd

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/formula.utilities.Rd

Lines changed: 11 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

man/sign-set.Rd

Lines changed: 16 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)