-
Notifications
You must be signed in to change notification settings - Fork 0
/
prog_utils.R
204 lines (199 loc) · 6.38 KB
/
prog_utils.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#' Like `message` but with a timestamp
#'
#' This is identical to the [message()] function, except that it
#' prepends a timestamp to every message.
#'
#' @param ... All arguments are passed directly to `message`.
#'
#' The time stamp is generated using [date()]. If you need anything
#' more complicated than this, it's probably easier to abondon this
#' function and just use `date()` and `message()` to manually add the
#' timestamp in the format you want.
#'
#' @examples
#'
#' tsmsg("Hello world")
#'
#' @seealso [message()], [date()]
#'
#' @export
tsmsg <- function(...) {
message(date(), ": ", ...)
}
#' Assign into complex sub-expressions and return the whole object
#'
#' @noMd
#'
#' This function exists to facilitate assignment to parts of an object
#' in the middle of a magrittr pipeline. Normally this is disruptive,
#' since assignment returns the value that was assigned, rather than
#' the whole object.
#'
#' @param x The object to assign into. (Typically this argument is
#' delivered via \code{\%\ > \%()}.)
#' @param expr The left hand side of the assignment operation to be
#' performed on \code{x}.
#' @param value The right hand side of the assignment operation to be
#' performed.
#'
#' As usual, \code{x}, the object being passed in, should be
#' referenced in both \code{expr} and \code{value} as \code{.}. In
#' fact, \code{expr} *must* contain at least one \code{.}, or else the
#' object will not be modified. (This is currently not checked.)
#'
#' Note that this function uses the lazyeval package rather than its
#' apparent successor, rlang, because rlang doesn't support
#' interpolating expressions on the left-hand-side of an assignment
#' operation: https://github.com/r-lib/rlang/issues/212.
#'
#' @examples
#'
#' library(magrittr)
#'
#' # Returns the entire list, not just the value of x
#' list(a = 1, b = 2, c = 3) %>% assign_into(.$x, 5)
#'
#' \dontrun{
#' # A more complex example of how this might be used in the middle of
#' # a pipeline. Imagine that x is a list of SummarizedExperiment
#' # objects, and for some reason one of the values in one of the
#' # assays in one of the objects is wrong and needs to be modified.
#' x %>% assign_into(assays(.[[1]])$counts[3,5], 45) %>% ...
#' }
#'
#' @seealso \code{\%\ > \%} from the magrittr package.
#'
#' @export
assign_into <- function(x, expr, value) {
req_ns("lazyeval")
## Required because `f_interp` doesn't seem to like `::`
uq <- lazyeval::uq
expr <- lazyeval::lazy(expr)$expr
lazyeval::f_eval(lazyeval::f_interp(~( x %>% { uq(expr) <- uq(value); . })))
}
#' Evaluate an expression and then collect garbage.
#'
#' Logically, this function is equivalent to [identity()], simply
#' returning the value of the expression passed to it. However, it
#' also ensures that after evaluating this expression, a garbage
#' collection run is performed, even if the expression throws an
#' error.
#'
#' I have found this function occasionally useful when dealing with
#' very large objects that bump up against the memory capacity of the
#' computer I am using. One common use case is loading a very large R
#' data file and keeping only one object from it while discarding the
#' rest. However, overuse of this function when it is not needed will
#' simply slow down your code.
#'
#' @param expr The expression to evaluate.
#' @param ... Further arguments are passed to [gc()].
#'
#' @examples
#'
#' with_gc({
#' # Create a "large" object (this one is not actually large because
#' # examples need to be kept small)
#' large_object <- 1:5000
#' # Return only a small piece of the object.
#' large_object[5]
#' })
#' # large_object has now been garbage-collected
#'
#' @seealso [gc()]
#'
#' @export
with_gc <- function(expr, ...) {
on.exit(gc(...))
return(expr)
}
#' Print a readable summary of a list of values.
#'
#' This is useful for printing out a list of the parsed command-line
#' arguments for a script. The output is generally more compact than
#' [print()] or [str()].
#'
#' @param v A named list or vector
#'
#' @return Returns `v` invisibly.
#'
#' @examples
#'
#' x <- list(verbose = TRUE, infile = "a.txt", outfile = "b.txt", ncores = 8)
#' print_var_vector(x)
#'
#' @importFrom rlang is_named
#' @export
print_var_vector <- function(v) {
assert_that(is_named(v))
for (i in names(v)) {
cat(i, ": ", deparse(v[[i]]), "\n", sep = "")
}
invisible(v)
}
#' Construct futures for a list of expressions
#'
#' Instead of evaluating the expressions in the list, this function
#' constructs a future for each expression and returns the list of
#' these futures.
#'
#' This is the standard-evaluation helper for [make_futures()], which
#' uses non-standard evaluation.
#'
#' @param expressions The list of unevaluated expressions for which to
#' construct futures. Each element can be anything that
#' [rlang::as_quosure()] will accept.
#' @param .future.args Additional arguments to pass to
#' [future::future()].
#'
#' @return A list of futures, whose values will be the result of
#' evaluating each of `expressions`.
#'
#' @seealso [rlang::eval_tidy()], [rlang::quo()], [rlang::as_quosure()], [future::values()]
#'
#' @examples
#'
#' library(future)
#' library(rlang)
#' expressions <- list(a = quo(1+1), b = quo(2+2))
#'
#' flist <- make_futures_(expressions)
#' flist
#' values(flist)
#'
#' # Same result without futures
#' lapply(expressions, eval_tidy)
#'
#' @importFrom rlang eval_tidy quo as_quosure as_list
#' @export
make_futures_ <- function(expressions, .future.args = list()) {
.future.args <- as_list(.future.args)
if (! "lazy" %in% names(.future.args)) {
.future.args$lazy <- TRUE
}
req_ns("future")
lapply(expressions, . %>% as_quosure %>% {quo(future::future(!!., !!!.future.args))} %>% eval_tidy)
}
#' Construct futures for several expressions
#'
#' Instead of evaluating its arguments, this function converts them
#' into futures using [future::future()]. Calling [future::value()] on
#' each one will yield the value of that expression.
#'
#' @param ... Expressions for which to construct futures.
#' @param .future.args Additional arguments to pass to
#' [future::future()].
#' @return A list of futures, one for each expression in `...`.
#'
#' @examples
#'
#' library(future)
#' flist <- make_futures(a = 1+1, b = 2+2)
#' flist
#' values(flist)
#'
#' @importFrom rlang enquos
#' @export
make_futures <- function(..., .future.args = list()) {
make_futures_(enquos(...), .future.args)
}