一种(可以说是更“正确”)的方法是为@BenBolker建议的迭代器编写自己的迭代器(写入扩展的pdf是
here).缺乏更正式的东西,这里是一个穷人的迭代器,类似于expand.grid但是手动推进. (注意:考虑到每次迭代的计算比这个函数本身“更昂贵”,这就足够了.这可以真正改进,但“它有效”.)
每次返回返回的函数时,此函数都会返回一个命名列表(带有提供的因子).它是懒惰的,因为它没有扩展整个可能的列表;它本身并不是懒惰,它们应该立即“消耗”.
lazyExpandGrid
dots
sizes
indices
function() {
indices[1] <
DONE
while (any(rolls sizes))) {
if (tail(rolls, n=1)) return(FALSE)
indices[rolls] <
indices[ 1+which(rolls) ] <
}
mapply(`[`, dots, indices, SIMPLIFY = FALSE)
}
}
样品用法:
nxt
nxt()
# a b c
# 1 1 15 21
nxt()
# a b c
# 1 2 15 21
nxt()
# a b c
# 1 3 15 21
nxt()
# a b c
# 1 1 16 21
##
nxt()
# a b c
# 1 3 16 22
nxt()
# [1] FALSE
注意:为了简洁显示,我使用as.data.frame(mapply(…))作为示例;它可以正常工作,但如果命名列表适合您,则无需转换为data.frame.
编辑
基于alexis_laz’s answer,这是一个大大改进的版本,它(a)更快,(b)允许任意搜索.
lazyExpandGrid
dots
argnames
if (is.null(argnames)) argnames
sizes
indices
maxcount
i
function(index) {
i <
if (length(i) > 1L) return(do.call(rbind.data.frame, lapply(i, sys.function(0))))
if (i > maxcount || i < 1L) return(FALSE)
setNames(Map(`[[`, dots, (i - 1L) %% indices[-1L] %/% indices[-length(indices)] + 1L ),
argnames)
}
}
它不使用任何参数(自动递增内部计数器),一个参数(搜索和设置内部计数器)或向量参数(寻找每个参数并将计数器设置为最后一个,返回data.frame).
最后一个用例允许对设计空间的子集进行采样:
set.seed(42)
nxt
as.data.frame(nxt())
# a b c d e f
# 1 1 1 1 1 1 1
nxt(sample(1e2^6, size=7))
# a b c d e f
# 2 69 61 7 7 49 92
# 21 72 28 55 40 62 29
# 3 88 32 53 46 18 65
# 4 88 33 31 89 66 74
# 5 57 75 31 93 70 66
# 6 100 86 79 42 78 46
# 7 55 41 25 73 47 94
感谢alexis_laz对cumprod,Map和索引计算的改进!