Мое решение очень похоже на решение Андри, за исключением того, что оно использует базу R, и я комментирую его необходимость в том, чтобы обернуть то, что вы хотите сделать в функции, и последующую необходимость использовать <<-
для изменения содержимого в более высокой среде..
Вот функция, которая ничего не делает и делает это медленно:
myfun <- function(x, text) {
Sys.sleep(0.2)
cat("running ",x, " with text of '", text, "'\n", sep="")
x
}
Вот моя forp
функция.Обратите внимание, что независимо от того, что мы на самом деле зацикливаем, вместо этого он зацикливается на последовательности 1:n
и получает правильный термин того, что мы на самом деле хотим в цикле.plyr
делает это автоматически.
library(tcltk)
forp <- function(x, FUN, ...) {
n <- length(x)
pb <- tkProgressBar(title = "Working hard:", min = 0, max = n, width = 300)
out <- vector("list", n)
for (i in seq_len(n)) {
out[[i]] <- FUN(x[i], ...)
setTkProgressBar(pb, i, label=paste( round(i/n*100, 0), "% ready!"))
}
close(pb)
invisible(out)
}
И вот как можно использовать for
и forp
, если все, что мы хотим сделать, это вызвать myfun
:
x <- LETTERS[1:5]
for(xi in x) myfun(xi, "hi")
forp(x, myfun, text="hi")
И вот как они могут быть использованы, если мы хотим что-то изменить по пути.
out <- "result:"
for(xi in x) {
out <- paste(out, myfun(xi, "hi"))
}
out <- "result:"
forp(x, function(xi) {
out <<- paste(out, myfun(xi, "hi"))
})
Для обеих версий результат будет
> out
[1] "result: A B C D E"
РЕДАКТИРОВАТЬ: После просмотра вашего (ДарокцигаРешение, у меня есть еще одна идея, которая может быть не такой уж громоздкой, которая заключается в оценке выражения в родительском фрейме.Это облегчает учет значений, отличных от i
(теперь указывается с помощью аргумента index
), хотя на данный момент я не думаю, что он обрабатывает функцию в качестве выражения, хотя просто добавлю вместоцикл, который не должен иметь значения.
forp2 <- function(index, x, expr) {
expr <- substitute(expr)
n <- length(x)
pb <- tkProgressBar(title = "Working hard:", min = 0, max = n, width = 300)
for (i in seq_len(n)) {
assign(index, x[i], envir=parent.frame())
eval(expr, envir=parent.frame())
setTkProgressBar(pb, i, label=paste( round(i/n*100, 0), "% ready!"))
}
close(pb)
}
Код для запуска моего примера сверху будет
out <- "result:"
forp2("xi", LETTERS[1:5], {
out <- paste(out, myfun(xi, "hi"))
})
, и результат будет таким же.
ДРУГОЕ РЕДАКТИРОВАНИЕна основании дополнительной информации в вашем предложении:
Возможен синтаксис forX(1:1000) %doX$ { expression }
;это то, что делает пакет foreach
.Мне сейчас лень строить его из вашего решения, но если строить из моего, это может выглядеть так:
`%doX%` <- function(index, expr) {
x <- index[[1]]
index <- names(index)
expr <- substitute(expr)
n <- length(x)
pb <- tkProgressBar(title = "Working hard:", min = 0, max = n, width = 300)
for (i in seq_len(n)) {
assign(index, x[i], envir=parent.frame())
eval(expr, envir=parent.frame())
setTkProgressBar(pb, i, label=paste( round(i/n*100, 0), "% ready!"))
}
close(pb)
invisible(out)
}
forX <- function(...) {
a <- list(...)
if(length(a)!=1) {
stop("index must have only one element")
}
a
}
Тогда синтаксис использования такой, а результат такой же, как и выше.
out <- "result:"
forX(xi=LETTERS[1:5]) %doX% {
out <- paste(out, myfun(xi, "hi"))
}
out