|
a |
|
b/R/bind_to_function.R |
|
|
1 |
#' Encloses argument in a function's environment |
|
|
2 |
#' |
|
|
3 |
#' This function takes a function \code{f} and a series of named arguments, and |
|
|
4 |
#' returns a closure of \code{f} which will only rely on one single argument |
|
|
5 |
#' 'param'. This is used to reduce the number of arguments passed around to |
|
|
6 |
#' likelihood or movement functions. This functionality is used internally when |
|
|
7 |
#' creating closures of custom moves in \code{bind_moves}. |
|
|
8 |
#' |
|
|
9 |
#' @param f The function to which arguments are bound. |
|
|
10 |
#' |
|
|
11 |
#' @param ... Named arguments to bind to the function's environment. |
|
|
12 |
#' |
|
|
13 |
#' @author Initial code by Rich FitzJohn (see 'references') with some |
|
|
14 |
#' adaptations by Thibaut Jombart. |
|
|
15 |
#' |
|
|
16 |
#' @references Initial code comes from the \code{partially_apply} function in |
|
|
17 |
#' the 'rodeint' package \code{richfitz/rodeint}. |
|
|
18 |
#' |
|
|
19 |
#' |
|
|
20 |
#' @export |
|
|
21 |
#' |
|
|
22 |
bind_to_function <- function(f, ...) { |
|
|
23 |
|
|
|
24 |
## We isolate the arguments of 'f' and identify those without defaults, |
|
|
25 |
## which need to be provided through '...'. Arguments of 'f' which have a |
|
|
26 |
## default value will be replaced with content of '...' if provided. The |
|
|
27 |
## function returned is a closure with a single argument 'param', and with |
|
|
28 |
## all non-default arguments in its environment. |
|
|
29 |
|
|
|
30 |
dots <- list(...) |
|
|
31 |
dots_names <- names(dots) |
|
|
32 |
f_args <- setdiff(names(formals(f)), "param") |
|
|
33 |
have_no_default <- vapply(formals(f)[f_args], is.symbol, logical(1)) |
|
|
34 |
f_args_no_default <- names(have_no_default)[have_no_default] |
|
|
35 |
|
|
|
36 |
|
|
|
37 |
## CHECKS ## |
|
|
38 |
if (is.primitive(f)) { |
|
|
39 |
stop("Cannot use with primitive functions") |
|
|
40 |
} |
|
|
41 |
|
|
|
42 |
## Nothing to do if nothing provided |
|
|
43 |
if (length(dots) == 0) { |
|
|
44 |
if (length(f_args) > 1) { |
|
|
45 |
stop("'...' is empty but 'f' has more than one argument.") |
|
|
46 |
} |
|
|
47 |
return(f) |
|
|
48 |
} |
|
|
49 |
|
|
|
50 |
## All objects passed in '...' need to be named |
|
|
51 |
if (is.null(dots_names) || !all(nzchar(dots_names))) { |
|
|
52 |
stop("All arguments provided through '...' need to be named.") |
|
|
53 |
} |
|
|
54 |
|
|
|
55 |
## Name duplication is not allowed |
|
|
56 |
if (any(duplicated(dots_names))) { |
|
|
57 |
duplicated_args <- paste(unique(dots_names[duplicated(dots_names)]), |
|
|
58 |
collapse = ", ") |
|
|
59 |
msg <- sprintf("Duplicated formal arguments: ", |
|
|
60 |
duplicated_args) |
|
|
61 |
|
|
|
62 |
stop(msg) |
|
|
63 |
} |
|
|
64 |
|
|
|
65 |
## ... cannot contain 'param' |
|
|
66 |
if ("param" %in% dots_names) { |
|
|
67 |
stop("'...' cannot contain an argument 'param'") |
|
|
68 |
} |
|
|
69 |
|
|
|
70 |
|
|
|
71 |
## make sure all arguments of 'f' which don't have default values but |
|
|
72 |
## 'param' are in '...' |
|
|
73 |
|
|
|
74 |
are_missing <- !f_args_no_default %in% dots_names |
|
|
75 |
if (any(are_missing)) { |
|
|
76 |
missing_args <- f_args_no_default[are_missing] |
|
|
77 |
missing_args <- paste(missing_args, collapse = ", ") |
|
|
78 |
msg <- sprintf("Arguments of %s missing from '...' with no default: %s", |
|
|
79 |
deparse(substitute(f)), |
|
|
80 |
missing_args) |
|
|
81 |
stop(msg) |
|
|
82 |
} |
|
|
83 |
|
|
|
84 |
## remove arguments that are not part of 'f' |
|
|
85 |
to_keep <- dots_names %in% f_args |
|
|
86 |
dots <- dots[to_keep] |
|
|
87 |
dots_names <- names(dots) |
|
|
88 |
|
|
|
89 |
|
|
|
90 |
## Attach arguments to 'f' |
|
|
91 |
add_to_function_environment(f, dots) |
|
|
92 |
} |
|
|
93 |
|
|
|
94 |
|
|
|
95 |
|
|
|
96 |
|
|
|
97 |
|
|
|
98 |
|
|
|
99 |
## This function adds a list of objects to a function's environment |
|
|
100 |
|
|
|
101 |
add_to_function_environment <- function(f, defaults) { |
|
|
102 |
e <- as.environment(defaults) |
|
|
103 |
parent.env(e) <- environment(f) |
|
|
104 |
ff <- formals(f) |
|
|
105 |
replace_formals(f, ff[c(setdiff(names(ff), names(defaults)))], e) |
|
|
106 |
} |
|
|
107 |
|
|
|
108 |
|
|
|
109 |
|
|
|
110 |
|
|
|
111 |
|
|
|
112 |
|
|
|
113 |
## This replaces forms, but preserves attributes except for srcref, |
|
|
114 |
## which will be invalid for any nontrivial change (and will |
|
|
115 |
## confusingly be printed with the wrong structure). |
|
|
116 |
|
|
|
117 |
replace_formals <- function(fun, value, envir = environment(fun)) { |
|
|
118 |
old_attributes <- attributes(fun) |
|
|
119 |
formals(fun, envir = envir) <- value |
|
|
120 |
attributes(fun) <- old_attributes[names(old_attributes) != "srcref"] |
|
|
121 |
fun |
|
|
122 |
} |