控制流程結構

For 迴圈是用於在域上重複任務或任務集的流控制方法。for 迴圈的核心結構是

for ( [index] in [domain]){
  [body]
}

哪裡

  1. [index] 是一個名稱,在迴圈的每次迭代中只取一個 [domain] 的值。
  2. [domain] 是要迭代的值的向量。
  3. [body] 是應用於每次迭代的指令集。

作為一個簡單的例子,考慮使用 for 迴圈來獲得值向量的累積和。

x <- 1:4
cumulative_sum <- 0
for (i in x){
  cumulative_sum <- cumulative_sum + x[i]
}
cumulative_sum

優化迴圈結構

for 迴圈可用於概念化和執行要重複的任務。但是,如果不仔細構造,與 apply 系列功能的首選使用相比,它們的執行速度可能非常慢。儘管如此,你可以在 for 迴圈結構中包含一些元素來優化迴圈。在許多情況下,for 迴圈的良好構造將產生非常接近於 apply 函式的計算效率。

正確構造的for 迴圈構建在核心結構上,幷包含一個語句,宣告將捕獲迴圈的每次迭代的物件。該物件應該宣告一個類和一個長度。

[output] <- [vector_of_length]
for ([index] in [length_safe_domain]){
  [output][index] <- [body]
}

為了說明,讓我們編寫一個迴圈來對數值向量中的每個值進行平方(這只是一個簡單的例子,僅用於說明。完成此任務的’正確’方法將是 x_squared <- x^2)。

x <- 1:100
x_squared <- vector("numeric", length = length(x))
for (i in seq_along(x)){
  x_squared[i] <- x[i]^2
}

再次注意,我們首先為輸出 x_squared 宣告瞭一個容器,並給它提供了與 x 相同長度的數字類。此外,我們使用 seq_along 函式宣告瞭長度安全域seq_along 為適合用於 for 迴圈的物件生成索引向量。雖然使用 for (i in 1:length(x)) 似乎很直觀,但如果 x 的長度為 0,則迴圈將嘗試在 1:0 的域上進行迭代,從而導致錯誤(R 中的第 0 個索引未定義)。

插座物件和長度安全域由 apply 系列功能在內部處理,鼓勵使用者儘可能採用 apply 方法代替 for 迴圈。但是,如果構造正確,for 迴圈可能偶爾提供更高的程式碼清晰度,同時效率損失最小。

向量化迴圈

For 迴圈通常可以成為概念化每次迭代中需要完成的任務的有用工具。當迴圈完全開發和概念化時,將迴圈轉換為函式可能是有利的。

在這個例子中,我們將開發一個 for 迴圈來計算 mtcars 資料集中每列的平均值(同樣,一個簡單的例子,因為它可以通過 colMeans 函式完成)。

column_mean_loop <- vector("numeric", length(mtcars))
for (k in seq_along(mtcars)){
  column_mean_loop[k] <- mean(mtcars[[k]])
}

通過將迴圈體重寫為函式,可以將 for 迴圈轉換為 apply 函式。

col_mean_fn <- function(x) mean(x)
column_mean_apply <- vapply(mtcars, col_mean_fn, numeric(1))

並比較結果:

identical(column_mean_loop, 
          unname(column_mean_apply)) #* vapply added names to the elements
                                     #* remove them for comparison

向量化形式的優點是我們能夠消除幾行程式碼。通過 apply 函式為我們處理確定輸出物件的長度和型別以及遍歷長度安全域的機制。此外,apply 函式比迴圈快一點。根據迭代次數和身體的複雜程度,人類的速度差異通常可以忽略不計。