为什么 `lapply` returns 自动分配结果?

Why `lapply` returns result of assignment automatically?

q <- lapply(1:3, function(x) x ** 2)
## returns nothing, because it is an assignment

# however, how you explain this?:

> lapply(list(1:3, 4:6, 7:9, 10:11), function(v) q <- lapply(v, function(x) x ** 2))
[[1]]
[[1]][[1]]
[1] 1

[[1]][[2]]
[1] 4

[[1]][[3]]
[1] 9


[[2]]
[[2]][[1]]
[1] 16

[[2]][[2]]
[1] 25

[[2]][[3]]
[1] 36


[[3]]
[[3]][[1]]
[1] 49

[[3]][[2]]
[1] 64

[[3]][[3]]
[1] 81


[[4]]
[[4]][[1]]
[1] 100

[[4]][[2]]
[1] 121

# while this gives the same but is logical (q is stated as return value).
> lapply(list(1:3, 4:6, 7:9, 10:11), function(v) {q <- lapply(v, function(x) x ** 2);q})
[[1]]
[[1]][[1]]
[1] 1

[[1]][[2]]
[1] 4

[[1]][[3]]
[1] 9


[[2]]
[[2]][[1]]
[1] 16

[[2]][[2]]
[1] 25

[[2]][[3]]
[1] 36


[[3]]
[[3]][[1]]
[1] 49

[[3]][[2]]
[1] 64

[[3]][[3]]
[1] 81


[[4]]
[[4]][[1]]
[1] 100

[[4]][[2]]
[1] 121

为什么在第二个表达式中,虽然里面的lapply只是赋值给了q,但是q并没有在函数结束时被调用,赋值的值 返回到外部lapply并因此被收集? 请问有人对这种现象有解释吗?

它也适用于 =

lapply(list(1:3, 4:6, 7:9, 10:11), function(v) q = lapply(c(v), function(x) x ** 2))

[[1]]
[[1]][[1]]
[1] 1

[[1]][[2]]
[1] 4

[[1]][[3]]
[1] 9


[[2]]
[[2]][[1]]
[1] 16

[[2]][[2]]
[1] 25

[[2]][[3]]
[1] 36


[[3]]
[[3]][[1]]
[1] 49

[[3]][[2]]
[1] 64

[[3]][[3]]
[1] 81


[[4]]
[[4]][[1]]
[1] 100

[[4]][[2]]
[1] 121

答案在于赋值操作return值。赋值运算符 <- 不仅将值写入调用环境中的变量,它实际上还无形地 return 将赋值本身发送给调用者。

记住 R 中的所有操作实际上都是函数。当你这样做时

x <- 3

你其实在做

`<-`(x, 3)

这不仅在调用环境中创建符号“x”并将值 3 分配给该符号,而且无形地return将值 3 分配给调用者.要看到这一点,请考虑:

y <- 2
y
#> [1] 2

y <- `<-`(x, 3)
y
#> [1] 3

或等价地,

y <- (x <- 4)
y
#> [1] 4

事实上,由于 R 的求值顺序,我们甚至可以这样做:

y <- x <- 5
y
#> [1] 5

这是在同一行中将多个变量设置为相同值的巧妙方法。

现在考虑在 lapply:

中使用的 lambda 函数
function(v) q <- lapply(v, function(x) x ** 2)

看看当我们将此功能视为独立功能时会发生什么:

func <- function(v) q <- lapply(v, function(x) x ** 2)

func(1:3)

正如预测的那样,没有任何反应。但是当我们这样做时会发生什么:

a <- func(1:3)

如果 func(1:3) 没有 return 任何东西,那么大概 a 现在应该是空的。

但这不是...

 a
#> [[1]]
#> [1] 1
#>
#> [[2]]
#> [1] 4
#>
#> [[3]]
#> [1] 9

因为赋值的值被 return 无形地传递给了调用者,我们能够将它赋值给调用范围内的值。因此,做

lapply(list(1:3, 4:6, 7:9, 10:11), function(v) q <- lapply(v, function(x) x ** 2))

将应用于所有列表元素的内部函数的值分配给新列表。此列表 不是 return 不可见的,而是 return 正常编辑的。

所以这是预期的行为。