2016-02-29 1 views
0

Я очень смущен тем, что происходит в этом примере.Функция, присвоенная списку внутри цикла for в R

Я пытаюсь написать function factory и передать ему список параметров для создания списка функций. Мне повезло с этим методом для простых функций. Теперь я пытаюсь выполнить более сложную задачу, которая использует список параметров. Мне не удалось заставить его работать с lapply, но я открыт для этого подхода. Я попробовал петлю, и все ведет себя странно.

library(TTR) 
data(ttrc) 
#last 100 closing prices for testing 
ttrc <- tail(ttrc['Close'], 100) 

#Function Factory for creating moving average functions 
MAFactory <- function(fun, n) function(x){ 
    setNames(get(fun)(x = x, n = n), paste(fun, n, sep = "_")) 
} 
#parameters to use with the funciton factory 
grid <- expand.grid(fncts = c("SMA", "EMA", "DEMA"), 
        ns = c(10, 20, 30), 
        stringsAsFactors = FALSE) 

MAfuns <- vector("list", nrow(grid)) 

#loop over the grid to apply the function factory 
for (i in 1:nrow(grid)) { 
    #print statements for debugging 
    print(i) 
    MAfuns[[i]] <- MAFactory(grid[i, 1], grid[i, 2]) 
    print(paste(grid[i,], collapse = '')) 
} 

#test cases 
sma10 <- MAFactory("SMA", 10) 
sma10Simple <- SMA(x = ttrc, n = 10) 

(sma10Simple == sma10(ttrc)) 
(MAfuns[[1]](ttrc) == sma10(ttrc)) 
(MAfuns[[1]](ttrc) == sma10Simple) 

#cause of failure 
tmpE <- environment(MAfuns[[1]]) 
mget(envir = tmpE, x = ls(envir = tmpE)) 
#all MAfuns use last parameters from grid??? 

#but it works outside the loop when i = 1?! 
MAfuns[[1]] <- MAFactory(grid[1, 1], grid[1, 2]) 
(MAfuns[[1]](ttrc) == sma10(ttrc)) 

Я думаю, что проблема имеет что-то делать с оценкой обещания, основываясь на этом question. Но исправление, вызывающее аргумент на фабрике функций, не работает для меня. Если по какой-то причине это невозможно сделать внутри цикла, как я могу переписать его с помощью лапши?

+0

Я считаю, что этот ответ должен быть вам полезен: http://stackoverflow.com/a/30131673/2372064 – MrFlick

+0

Ваш пример не воспроизводится. Откуда «Cl»? Вероятно, вам придется использовать «force» для обеспечения оценки одного из ваших аргументов в окружающих средах функциях. У меня есть некоторые примечания по этой проблеме вы можете найти здесь: https://wahani.github.io/2014/09/Promises-and-Closures-in-R/ – Sebastian

+0

Извините. Cl - из пакета quantmod. Я отредактировал сценарий так, чтобы он вам не нужен. – brandco

ответ

0

Я придумал ответ на свой вопрос. Я не знаю, может ли он пролить свет на то, что происходит с закрытием/lazy eval/for loop, но это решает проблему.

library(TTR) 
data(ttrc) 
#last 100 closing prices for testing 
ttrc <- tail(ttrc['Close'], 100) 

#Function Factory for creating moving average functions 
MAFactory <- function(fun, n) function(x){ 
    get(fun)(x = x, n = n) 
} 
#parameters to use with the funciton factory 
grid <- expand.grid(fncts = c("SMA", "EMA", "DEMA"), 
        ns = c(10, 20, 30), 
        stringsAsFactors = FALSE) 

###__Answer__### 
#create the functions in a list 
MAfuns <- lapply(1:nrow(grid), function(i) get(grid[i,1])) 
#name the functions 
names(MAfuns) <- lapply(1:nrow(grid), function(i) paste(grid[i,], 
                  collapse = '')) 
#change the formals of each function in the list directly 
for (i in 1:nrow(grid)) {formals(MAfuns[[i]])$n <- grid$ns[i]} 
### 

sma10 <- MAFactory("SMA", 10) 
sma10Simple <- SMA(x = ttrc, n = 10) 

(sma10Simple == sma10(ttrc)) 
(MAfuns[[1]](ttrc) == sma10(ttrc)) 

То, что я пытаюсь сделать здесь создать длинный список функций с различными аргументами, без необходимости копировать и вставлять или повторить мой код. Большинство примеров такого рода шаблонов, которые я видел в Интернете, включая то, что связано с этим вопросом, включают функции с одним аргументом. Но что вы будете делать, если у вас есть два аргумента, которые вы хотите изменить? Или более одного функции с тем же списком аргументов?

Этот ответ решает эту проблему, непосредственно управляя функцией formals и избегая родительской среды и ленивых проблем оценки.

0

Я все еще думаю, что проблема, с которой вы сталкиваетесь, описана в сообщении, на которое я ссылался в комментарии выше. Ваши решения, похоже, более востребованы, чем необходимо, поэтому я хочу сделать другое предложение. Что нам нужно в качестве решения, так это то, что аргументы MAFactory оцениваются в окружении возвращаемой функции. Вот для чего предназначена функция force. Это заставляет рано оценивать обещание. Поскольку у вас есть функция с несколькими аргументами (MAFactory) lapply не выполняет работу - вам нужна многомерная карта. В R многомерное отображение называется mapply в соответствии с семейством функций *apply или Map. mapply попытается упростить свои результаты, и это то, что я всегда получаю по привычке, если я этого не хочу.

library(TTR) 
data(ttrc) 
#last 100 closing prices for testing 
ttrc <- tail(ttrc['Close'], 100) 

#Function Factory for creating moving average functions 
MAFactory <- function(fun, n) { 
    force(fun) 
    force(n) 
    function(x){ 
     get(fun)(x = x, n = n) 
    } 
} 

MAfuns <- mapply(
    MAFactory, 
    c("SMA", "EMA", "DEMA"), 
    c(10, 20, 30), 
    SIMPLIFY = FALSE 
) 


#test cases 
sma10 <- MAFactory("SMA", 10) 
sma10Simple <- SMA(x = ttrc, n = 10) 

identical(sma10Simple, sma10(ttrc)) 
identical(MAfuns[[1]](ttrc), sma10(ttrc)) 
identical(MAfuns[[1]](ttrc), sma10Simple) 

В вашем исходном примере вы хотите использовать функцию setNames, которая нуждается в векторе имен тех же длины, что и первый аргумент - то, что не является обязательным. Однако это не связано, я думаю, с вашим реальным вопросом.

Смежные вопросы