R语言for循环中列表更新不保留、向量更新保留的原因咨询
列表循环更新未保留的原因解析
问题描述
运行以下R代码后,向量tmp的更新在循环结束后得以保留,但列表ds却回到了初始状态,无法理解其中原因:
a <- array(seq(24), dim = c(4, 3, 2)) # 固定并完整读取的轴 axis = c(3) d <- dim(a) ds <- lapply(d, seq) # 初始化用于切片的列表 ds[-axis] <- 1 # 待迭代的轴 axis_iter <- seq_along(d)[-axis] # 迭代次数 n_iter <- prod(d[-axis]) order <- rev(seq_along(d[-axis])) # 仅用于演示 tmp <- rep(NA, n_iter) for (iter in seq(n_iter)) { tmp[iter] <- iter # 显示ds内的值被更新 print(unlist(ds)) # 切片数组并计算最小值(仅用于演示) print(min(do.call(`[`, c(list(a), ds, list(drop = TRUE))))) for (i in order) { ds[axis_iter][[i]] <- ds[axis_iter][[i]] + 1 if (ds[axis_iter][[i]] <= d[i]) break else ds[axis_iter][[i]] <- 1 } }
循环结束后ds的输出:
> ds [[1]] [1] 1 [[2]] [1] 1 [[3]] [1] 1 2
原因分析
核心问题出在循环中更新ds的方式:ds[axis_iter][[i]] <- ...。
- 在R中,
ds[axis_iter]会返回原列表ds的子集副本,而非对原列表元素的直接引用。后续对这个副本的修改只会作用于临时生成的副本上,完全不会改变原列表ds的内容。 - 而向量
tmp的更新是直接通过索引(tmp[iter] <- iter)操作原对象,所以修改会被保留。
解决方法
要直接修改原列表ds的元素,需要直接定位到原列表的对应位置,而非先创建子集副本。可以把循环内的更新代码改成:
for (i in order) { # 直接通过axis_iter的索引定位原列表ds的元素 ds[[axis_iter[i]]] <- ds[[axis_iter[i]]] + 1 if (ds[[axis_iter[i]]] <= d[axis_iter[i]]) break else ds[[axis_iter[i]]] <- 1 }
或者更简洁的写法:
for (i in order) { pos <- axis_iter[i] ds[[pos]] <- ds[[pos]] + 1 if (ds[[pos]] <= d[pos]) break else ds[[pos]] <- 1 }
这样修改后,循环结束时ds的元素会保留所有更新后的状态。
内容的提问来源于stack exchange,提问作者stschn
相关产品推荐
相关产品推荐

