使用列表在for循环中创建函数

时间:2016-08-08 07:45:45

标签: r

我对以下问题感到头疼:

我在for循环中创建了两个函数,其参数依赖于某些数据帧。然后将每个函数放在一个列表中。

打印for循环内的参数表明每个函数都已明确定义。然而,当我使用循环外的那些时,只有最后一个参数用于这两个函数。以下示例应该更清楚。

dt <- data.frame(color = c("red", "blue"),
                 a = c(3,9),
                 b = c(1.3, 1.8))
function_list <- list()
for (col in dt$color) {
  a <- dt$a[dt$color == col]
  b <- dt$b[dt$color == col]

  foo <- function(x) {
      a*x^b
  }
  print(paste(col, foo(1)))
  function_list[[col]] <- foo
}
  

[1]“红色3”

     

[1]“蓝色9”

function_list[["red"]](1)
  

[1] 9

function_list[["blue"]](1)
  

[1] 9

需要注意的是,这受到以下问题的启发:R nested for loop to write multiple functions and plot them

assignget的等效解决方案有效(我对上一个问题的回答)。

3 个答案:

答案 0 :(得分:0)

ab的相关值是您调用函数时的值,而不是您定义函数时的值。您创建列表的方式是从全局环境中获取。解决方案是创建closures。我会使用Map,但您可以使用for循环执行相同操作:

funs <- Map(function(a, b) function(x) a*x^b, a = dt$a, b = dt$b)

print(funs)
#[[1]]
#function (x) 
#a * x^b
#<environment: 0x000000000a9a4298>
#
#[[2]]
#function (x) 
#a * x^b
#<environment: 0x000000000a9a3728>

请注意不同的环境。

environment(funs[[1]])$a
#[1] 3
environment(funs[[2]])$a
#[1] 9

funs[[1]](1)
#[1] 3
funs[[2]](1)
#[1] 9

答案 1 :(得分:0)

通过Environments

进一步深入解决您的困惑

让我们检查一下您的代码无效的原因。当我尝试print(function_list)时,您会发现存储的两个函数都将返回a*x^b

# Part 1 : Why it doesn't work
# --------------------------

print(function_list)
# $red
# function (x) 
# {
#   a * x^b
# }
# 
# $blue
# function (x) 
# {
#   a * x^b
# }

如果您尝试删除a并重新运行该函数,则会返回错误。

rm(a)
function_list[['red']](1)
# Error in function_list[["red"]](1) : object 'a' not found

现在关于如何使代码正常工作:

有多种方法可以使其工作,其中大部分方法都需要使用环境或更改数据结构。

管理环境的一种方法 - 以保持您的值而不是在全局环境中搜索变量的方式 - 从函数返回一个函数。

# Part 2 : How to make it work
# ----------------------------

function_list <- list()
for (col in dt$color) {

  a <- dt$a[dt$color == col]
  b <- dt$b[dt$color == col]

  foo1 <- function(inner.a, inner.b) {
    return(function(x) {inner.a*x^inner.b})
  }

  foo2 <- foo1(a,b)
  print(paste(col, foo2(1)))

  function_list[[col]] <- foo2
}

现在,如果我们检查function_list中的内容,您可以看到这些功能在两个环境中

print(function_list)

# $red
# function (x) 
# {
#   inner.a * x^inner.b
# }
# <environment: 0x186fb40>
#   
#   $blue
# function (x) 
# {
#   inner.a * x^inner.b
# }
# <environment: 0x2536438>

输出也如预期。即使我们删除a,它仍然会按预期工作。

function_list[['red']](1) # 3
function_list[['blue']](1) # 9

rm(a)
function_list[['red']](1) #[1] 3

答案 2 :(得分:0)

我认为for循环不会创建新环境(您可以在循环中通过print(environment)进行检查),因此ab的值由{{ 1}}在它们为9和1.8的全局环境中,即它们最后指定的值。