【问题标题】:Function assigned to list inside of for loop in R分配给R中for循环内部列表的函数
【发布时间】:2016-06-11 22:13:03
【问题描述】:

我对这个示例中发生的事情感到非常困惑。

我正在尝试编写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。但是在函数工厂中调用参数的修复对我不起作用。如果由于某种原因这不能在循环内完成,我怎么能用 lapply 重写它?

【问题讨论】:

  • 我相信这个答案应该对你有帮助:stackoverflow.com/a/30131673/2372064
  • 您的示例不可重现。 Cl 来自哪里?您可能必须使用force 来强制在封闭环境的函数中评估您的参数之一。我有一些关于这个问题的说明,你可以在这里找到:wahani.github.io/2014/09/Promises-and-Closures-in-R
  • 对不起。 Cl 来自 quantmod 包。我编辑了脚本,所以你不需要它。
  • 不错的博文,感谢您的链接。我在 MAFactory 中使用force 评估n,但它不起作用。在发布此内容后的第二天,我通过重新使用 out for 循环解决了我的实际问题。但是,我仍然不确定我是否理解惰性评估,因为我无法弄清楚为什么这个示例不起作用。

标签: r function loops functional-programming


【解决方案1】:

我想出了我自己问题的答案。我不知道它是否可以阐明闭包/惰性 eval/for 循环问题发生了什么,但它确实解决了问题。

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 并同时避免父环境和惰性评估问题来解决此问题。

【讨论】:

    【解决方案2】:

    我仍然认为我在上面评论中引用的帖子中描述了您面临的问题。您的解决方案似乎比必要的更多,这就是我想提出不同建议的原因。作为解决方案,我们需要的是 MAFactory 的参数在返回函数的封闭环境中进行评估。这就是函数force 的用途。它强制对承诺进行早期评估。由于您有一个具有多个参数的函数 (MAFactory) lapply 没有完成这项工作 - 您需要一个多元映射。在 R 中,多元映射被命名为 mapply,对应于 *apply 系列函数或 Mapmapply 将尝试简化它的结果,除非我有意识地想要它,否则我总是会养成这种习惯。

    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,它需要一个与第一个参数长度相同的名称向量——这不一定是这种情况。但是,我认为这与您的真正问题无关。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-05
      • 2017-07-01
      • 2021-06-29
      • 1970-01-01
      • 2021-11-09
      • 2017-02-09
      • 1970-01-01
      • 2021-07-25
      相关资源
      最近更新 更多