Разделите аргументы `...` и распределите их по нескольким функциям
Используя следующую функцию foo()
В качестве простого примера я хотел бы распределить значения, приведенные в ...
две разные функции, если это возможно.
foo <- function(x, y, ...) {
list(sum = sum(x, ...), grep = grep("abc", y, ...))
}
В следующем примере я хотел бы na.rm
быть переданным sum()
, а также value
быть переданным grep()
, Но я получаю ошибку за неиспользованный аргумент в grep()
,
X <- c(1:5, NA, 6:10)
Y <- "xyzabcxyz"
foo(X, Y, na.rm = TRUE, value = TRUE)
# Error in grep("abc", y, ...) : unused argument (na.rm = TRUE)
Похоже, что аргументы были отправлены grep()
первый. Это верно? Я думаю, что R увидит и оценит sum()
во-первых, и вернуть ошибку для этого случая.
Кроме того, при попытке разделить аргументы в ...
Я столкнулся с проблемой. sum()
Формальные аргументы NULL
потому что это .Primitive
и поэтому я не могу использовать
names(formals(sum)) %in% names(list(...))
Я также не хочу предполагать, что оставшиеся аргументы
names(formals(grep)) %in% names(list(...))
должны быть автоматически переданы sum()
,
Как я могу безопасно и эффективно распространять ...
аргументы для нескольких функций, чтобы не делать ненужных оценок?
В долгосрочной перспективе я хотел бы иметь возможность применить это к функциям с длинным списком ...
аргументы, похожие на те из download.file()
а также scan()
,
5 ответов
Отдельные списки Если вы действительно хотите передать разные наборы параметров разным функциям, то, вероятно, лучше указать отдельные списки:
foo <- function(x, y, sum = list(), grep = list()) {
list(sum = do.call("sum", c(x, sum)), grep = do.call("grep", c("abc", y, grep)))
}
# test
X <- c(1:5, NA, 6:10)
Y <- "xyzabcxyz"
foo(X, Y, sum = list(na.rm = TRUE), grep = list(value = TRUE))
## $sum
## [1] 55
##
## $grep
## [1] "xyzabcxyz"
Гибридный список /... Альтернативой является то, что мы могли бы использовать... для одного из них, а затем указать другой в качестве списка, особенно в случае, если один из них часто используется, а другой редко используется. Часто используемый будет передаваться через..., а редко используемый через список. например
foo <- function(x, y, sum = list(), ...) {
list(sum = do.call("sum", c(x, sum)), grep = grep("abc", y, ...))
}
foo(X, Y, sum = list(na.rm = TRUE), value = TRUE)
Вот пара примеров гибридного подхода от самого R:
я) mapply
функция использует этот подход, используя оба ...
и MoreArgs
список:
> args(mapply)
function (FUN, ..., MoreArgs = NULL, SIMPLIFY = TRUE, USE.NAMES = TRUE)
NULL
II) nls
также использует этот подход, используя оба ...
и control
список:
> args(nls)
function (formula, data = parent.frame(), start, control = nls.control(),
algorithm = c("default", "plinear", "port"), trace = FALSE,
subset, weights, na.action, model = FALSE, lower = -Inf,
upper = Inf, ...)
NULL
Почему
grep
ошибка доsum
?Видеть, что
sum
намного более любезен со своими аргументами:X <- c(1:5, NA, 6:10) sum(X, na.rm = TRUE, value = TRUE) ## [1] 56
Он не потерпел неудачу, потому что не заботится о других именованных аргументах, поэтому
value = TRUE
упрощает простоTRUE
который составляет 1. Кстати:sum(X, na.rm = TRUE) ## [1] 55
Как разделить
...
к разным функциям?Один из методов (который очень подвержен ошибкам) - поиск аргументов для целевых функций. Например:
foo <- function(x, y, ...){ argnames <- names(list(...)) sumargs <- intersect(argnames, names(as.list(args(sum)))) grepargs <- intersect(argnames, names(as.list(args(grep)))) list(sum = do.call(sum, c(list(x), list(...)[sumargs])), grep = do.call(grep, c(list("abc", y), list(...)[grepargs]))) }
Это может привести к ошибкам в любое время, когда аргументы, используемые функцией, не сообщаются должным образом
args
такие как объекты S3. В качестве примера:names(as.list(args(plot))) ## [1] "x" "y" "..." "" names(as.list(args(plot.default))) ## [1] "x" "y" "type" "xlim" "ylim" ## [6] "log" "main" "sub" "xlab" "ylab" ## [11] "ann" "axes" "frame.plot" "panel.first" "panel.last" ## [16] "asp" "..." ""
В этом случае вы можете заменить соответствующую функцию S3. Из-за этого у меня нет обобщенного решения для этого (хотя я не знаю, что оно существует или не существует).
ТЛ;ДР
Рассмотрите возможность использования «трюка», реализованного в и других местах. Это проверено и проверено.
foo <- function(x, y, ...) {
localSum <- function(..., value) sum(...)
localGrep <- function(..., na.rm) grep(...)
list(sum = localSum(x, ...), grep = localGrep("abc", y, ...))
}
X <- c(1:5, NA, 6:10)
Y <- "xyzabcxyz"
foo(X, Y, na.rm = TRUE, value = TRUE)
## $sum
## [1] 55
##
## $grep
## [1] "xyzabcxyz"
##
Я немного шокирован тем, что «трюк», использованный здесь, еще не появился. (Может быть, это где-то упоминается?)
Напомним, что необходимо обрабатывать необязательные аргументы внутреннегоplot.xy
(для точек и линий), а также , , и . Все они обрабатываются с помощью одного аргумента.
Важно, когда мы проходим (скажем)lwd = 6
Чтобы расширить линии в области графика, необходимо убедиться, что этот аргумент не виден с помощью и , которые используются для установки ширины границы области графика и осевых линий. Следовательно:
> plot.default(0:1, 0:1, type = "l", lwd = 6)
Данная возможность реализована с помощью «локальных» версий подфункций.plot.window
,box
,axis
, и . Локальные версии представляют собой просто оболочки, предназначенные для фильтрации аргументов, которые нельзя разрешить видеть подфункциям (в данном случае , , , , и ).
> plot.default
function (x, y = NULL, type = "p", xlim = NULL, ylim = NULL,
log = "", main = NULL, sub = NULL, xlab = NULL, ylab = NULL,
ann = par("ann"), axes = TRUE, frame.plot = axes, panel.first = NULL,
panel.last = NULL, asp = NA, xgap.axis = NA, ygap.axis = NA,
...)
{
localAxis <- function(..., col, bg, pch, cex, lty, lwd) Axis(...)
localBox <- function(..., col, bg, pch, cex, lty, lwd) box(...)
localWindow <- function(..., col, bg, pch, cex, lty, lwd) plot.window(...)
localTitle <- function(..., col, bg, pch, cex, lty, lwd) title(...)
xlabel <- if (!missing(x))
deparse1(substitute(x))
ylabel <- if (!missing(y))
deparse1(substitute(y))
xy <- xy.coords(x, y, xlabel, ylabel, log)
xlab <- if (is.null(xlab))
xy$xlab
else xlab
ylab <- if (is.null(ylab))
xy$ylab
else ylab
xlim <- if (is.null(xlim))
range(xy$x[is.finite(xy$x)])
else xlim
ylim <- if (is.null(ylim))
range(xy$y[is.finite(xy$y)])
else ylim
dev.hold()
on.exit(dev.flush())
plot.new()
localWindow(xlim, ylim, log, asp, ...)
panel.first
plot.xy(xy, type, ...)
panel.last
if (axes) {
localAxis(if (is.null(y))
xy$x
else x, side = 1, gap.axis = xgap.axis, ...)
localAxis(if (is.null(y))
x
else y, side = 2, gap.axis = ygap.axis, ...)
}
if (frame.plot)
localBox(...)
if (ann)
localTitle(main = main, sub = sub, xlab = xlab, ylab = ylab,
...)
invisible()
}
<bytecode: 0x10b813010>
<environment: namespace:graphics>
Возьмем в качестве примера:
localTitle <- function(..., col, bg, pch, cex, lty, lwd) title(...)
plot.default
передает все аргументы при вызове
localTitle(main = main, sub = sub, xlab = xlab, ylab = ylab, ...)
ноtitle
видит только те аргументы, которые не названыcol
,bg
,pch
,cex
,lty
, илиlwd
, поскольку они включены в качестве формальных аргументовlocalTitle
.
Примечательно, что этот подход к обработке имеет минимальные накладные расходы, поскольку в полной мере использует преимущества ленивых вычислений. Ни одно из выражений в...
оцениваются до тех пор, пока не будут использованы подфункциями.
Вы можете только передать ...
аргумент другой функции, если эта другая функция включает в себя все именованные аргументы, которые вы передаете ...
или если у него есть ...
сам аргумент. Таким образом, для sum
это не проблема (args(sum)
возвращается function (..., na.rm = FALSE)
). С другой стороны grep
не имеет ни na.rm
ни ...
в качестве аргумента.
args(grep)
# function (pattern, x, ignore.case = FALSE, perl = FALSE, value = FALSE,
# fixed = FALSE, useBytes = FALSE, invert = FALSE)
Это не включает ...
а также не включает именованный аргумент na.rm
или. Простое решение состоит в том, чтобы просто определить свою собственную функцию mygrep
следующее:
mygrep <- function (pattern, x, ignore.case = FALSE, perl = FALSE, value = FALSE,
fixed = FALSE, useBytes = FALSE, invert = FALSE, ...)
grep(pattern, x, ignore.case, perl, value, fixed, useBytes, invert)
Тогда похоже на работу:
foo <- function(x, y, ...){
list(sum = sum(x, ...), grep = mygrep("abc", y, ...))
}
X <- c(1:5, NA, 6:10)
Y <- "xyzabcxyz"
foo(X, Y, na.rm = TRUE, value = TRUE)
# $sum
# [1] 56
#
# $grep
# [1] "xyzabcxyz"
Этот ответ не отвечает непосредственно на исходный вопрос, но может быть полезен другим, у которых возникает аналогичная проблема со своими собственными функциями (в отличие от существующих функций, таких как
sum
а также
grep
).
user2591234 содержит понимание, которое указывает на очень простое решение в таких случаях: просто убедитесь, что ваши вложенные функции имеют аргумент, и вы не получите
unused argument
ошибка.
Например:
nested1 <- function(x, a) {
x + a
}
nested2 <- function(x, b) {
x - b
}
f <- function(x, ...) {
if (x >= 0) {
nested1(x, ...)
} else {
nested2(x, ...)
}
}
Если мы позвоним, то получим ошибку:
Error in nested1(x, ...) : unused argument (b = 4)
.
Но просто добавьте
...
к формальностям
nested1
а также
nested2
и снова запустить:
nested1 <- function(x, a, ...) {
x + a
}
nested2 <- function(x, b, ...) {
x - b
}
В настоящее время,
f(x = 2, a = 3, b = 4)
дает желаемый результат:
5
. Задача решена.