R 有没有办法说 "do n times" 而不必编写带有冗余变量的 for 循环?

Does R have a way to say "do n times" without having to write a for loop with a redundant variable?

I recently wrote 这行代码:for(i in seq_len(exponent)){out<-squareMat%*%out}。显然,从未使用过 i,我只想说“exponent 次,做 out<-squareMat%*%out”。没有冗余计数器 i 有没有办法做到这一点?例如,是否有 apply 家庭功能?

示例 - 我有:

squareMat<-matrix(c(0,10,20,30),2,2)
out<-diag(nrow = nrow(squareMat))
exponent<-5
for(i in seq_len(exponent)){out<-squareMat%*%out}
out

我想要的是:

squareMat<-matrix(c(0,10,20,30),2,2)
out<-diag(nrow = nrow(squareMat))
exponent<-5
[do exponent times]{out<-squareMat%*%out}
out

这是一个使用 sapply 的例子。

myfunc <- function(s) {
    return('hello')
}
    
sapply(seq_len(5), myfunc)
    
# [1] "hello" "hello" "hello" "hello" "hello"

是的,确实如此。 base R 中有几个有用的函数,这些函数现在不常使用。其中之一完全符合您的要求。 replicate 函数将表达式 (expr) 复制 n 次。它的工作原理如下,假设我想从均匀(0 到 1)分布生成 3 个大小为 5 的不同样本。使用 replicate 可以轻松完成。看看下面的这段代码

replicate(n = 3, expr = {runif(n = 5)})
#            [,1]      [,2]       [,3]
# [1,] 0.1944426 0.5158065 0.39892501
# [2,] 0.5676580 0.9940599 0.97385575
# [3,] 0.5570141 0.2274214 0.60239883
# [4,] 0.5074303 0.3526040 0.95445298
# [5,] 0.1931812 0.4593620 0.03283596

结果自动组织成 array(在本例中为矩阵)。但是,您可以设置参数simplify = FALSE。然后,return 将是一个列表

replicate(n = 3, expr = {runif(n = 5)}, simplify = FALSE)
# [[1]]
# [1] 0.4694347 0.9559887 0.8110113 0.7528089 0.6639614
# 
# [[2]]
# [1] 0.8731027 0.7295846 0.3773571 0.5394776 0.6792322
# 
# [[3]]
# [1] 0.3463870 0.3776352 0.3895620 0.2166284 0.5065204

重要的是要注意这些复制中的每一个都是相互独立的。如果你想按顺序复制某些东西,你必须使用 for 循环或其他合适的函数。例如,存在一个名为 rapply 的函数(递归 lapply)。但是,我一直不清楚使用它的最佳方式。

对于像 %*% 这样的二元运算,您可以使用 Reduce,可选地使用 init 参数(实际上这里不需要,因为 init 是单位矩阵,请参阅@尼古拉的评论,但使回答更笼统):

squareMat<-matrix(c(0,10,20,30),2,2)
exponent<-5
Reduce('%*%', init = diag(nrow = nrow(squareMat)), replicate(exponent, squareMat, simplify=F))
#>          [,1]     [,2]
#> [1,]  7800000 27800000
#> [2,] 13900000 49500000

这可以通过在计算中不使用第二个参数来加速避免调用 replicate1:exponent 仅充当计数器):

Reduce(function(x,notused) {squareMat %*% x}, init = diag(nrow = nrow(squareMat)), 1:exponent)
#>          [,1]     [,2]
#> [1,]  7800000 27800000
#> [2,] 13900000 49500000

请注意,在这种情况下,Reduce 的效率低于简单循环,因此,有趣但可能不是最优的:

microbenchmark::microbenchmark(test_reduce(),test_reduce2(),test_loop())
Unit: microseconds
           expr  min   lq   mean median    uq  max neval cld
  test_reduce() 17.2 17.7 21.461   18.0 18.60 97.4   100   c
 test_reduce2()  7.9  8.4  9.463    8.6  8.85 25.9   100  b 
    test_loop()  3.0  3.2  3.786    3.3  3.40 26.2   100 a  

另一种方法是在函数调用中使用 Recall()

my_mat_power = function(n) {
  if (n == 1) 
    squareMat 
  else 
    Recall(n - 1) %*% squareMat
}

my_mat_power(5)

##          [,1]     [,2]
## [1,]  7800000 27800000
## [2,] 13900000 49500000

如果你想让你的矩阵成为一个变量,你可以做一些小的修改:

my_mat_power = function(n, mat) {
  if (n == 1) 
    mat 
  else 
    Recall(n - 1, mat) %*% mat
}

让我们看看这个:

library(expm)
squareMat %^% 5

R 几乎总有办法避免循环。你的情况:

library(expm)
out <- (squareMat%^%exponent)%*%out