8.3 循环

在R中有三种循环:repeatwhilefor。虽然向量化意味着你可能并不像其他语言一样大量需要它们,但在需要重复执行代码时,它们还是很有用的。

8.3.1 重复循环

R中最容易掌握的循环是repeat。它所做的事情就是反复地执行代码,直到告诉它停为止。在其他语言中,一般使用do while或其他类似的方法完成。以下的例子1将反复执行,直到你按下Escape键、退出R或世界末日降临为止:

1 如果这些例子对你来说没有意义,请观看电影http://www.imdb.com/title/tt0107048

  1. repeat
  2. {
  3. message("Happy Groundhog Day!")
  4. }

一般来说,我们还是希望在世界末日降临之前终止代码,因此需要一个break语句以跳出无限循环。在下例中,sample函数将在每个循环迭代中返回一个操作:

  1. repeat
  2. {
  3. message("Happy Groundhog Day!")
  4. action <- sample(
  5. c(
  6. "Learn French",
  7. "Make an ice statue",
  8. "Rob a bank",
  9. "Win heart of Andie McDowell"
  10. ),
  11. 1
  12. )
  13. message("action = ", action)
  14. if(action == "Win heart of Andie McDowell") break
  15. }
  16. ## Happy Groundhog Day!
  17. ## action = Rob a bank
  18. ## Happy Groundhog Day!
  19. ## action = Rob a bank
  20. ## Happy Groundhog Day!
  21. ## action = Rob a bank
  22. ## Happy Groundhog Day!
  23. ## action = Win heart of Andie McDowell

有时候,我们想做的不是退出整个循环,而是跳过当前的迭代,开始next下一次迭代而已:

  1. repeat
  2. {
  3. message("Happy Groundhog Day!")
  4. action <- sample(
  5. c(
  6. "Learn French",
  7. "Make an ice statue",
  8. "Rob a bank",
  9. "Win heart of Andie McDowell"
  10. ),
  11. 1
  12. )
  13. if(action == "Rob a bank")
  14. {
  15. message("Quietly skipping to the next iteration")
  16. next
  17. }
  18. message("action = ", action)
  19. if(action == "Win heart of Andie McDowell") break
  20. }
  21. ## Happy Groundhog Day!
  22. ## action = Learn French
  23. ## Happy Groundhog Day!
  24. ## Quietly skipping to the next iteration
  25. ## Happy Groundhog Day!
  26. ## Quietly skipping to the next iteration
  27. ## Happy Groundhog Day!
  28. ## action = Make an ice statue
  29. ## Happy Groundhog Day!
  30. ## action = Make an ice statue
  31. ## Happy Groundhog Day!
  32. ## Quietly skipping to the next iteration
  33. ## Happy Groundhog Day!
  34. ## action = Win heart of Andie McDowell

8.3.2 while循环

while循环就像是延迟了的repeat循环。它不是先执行代码再检查循环是否应该结束,而是先进行检查再(可能)执行代码。因为检查发生在开始时,所以循环体有可能不会被执行(与repeat循环不同)。在下例中,与以上repeat的例子类似,除了当“Win heart of Andie McDowell”被抽中了,剩下的Groundhog Day循环语句则可完全避免:

  1. action <- sample(
  2. c(
  3. "Learn French",
  4. "Make an ice statue",
  5. "Rob a bank",
  6. "Win heart of Andie McDowell"
  7. ),
  8. 1
  9. )
  10. while(action != "Win heart of Andie McDowell")
  11. {
  12. message("Happy Groundhog Day!")
  13. action <- sample(
  14. c(
  15. "Learn French",
  16. "Make an ice statue",
  17. "Rob a bank",
  18. "Win heart of Andie McDowell"
  19. ),
  20. 1
  21. )
  22. message("action = ", action)
  23. }
  24. ## Happy Groundhog Day!
  25. ## action = Make an ice statue
  26. ## Happy Groundhog Day!
  27. ## action = Learn French
  28. ## Happy Groundhog Day!
  29. ## action = Make an ice statue
  30. ## Happy Groundhog Day!
  31. ## action = Learn French
  32. ## Happy Groundhog Day!
  33. ## action = Make an ice statue
  34. ## Happy Groundhog Day!
  35. ## action = Win heart of Andie McDowell

使用一些小技巧能把repeat循环转换为while循环,或把while循环转换为loop循环,但通常使用其中一种语法会更简洁。如果你知道循环体必须至少执行一次,请使用repeat,否则使用while

8.3.3 for循环

第三种循环适用于已知代码所需执行的循环次数的情形。for循环将接受一个迭代器变量和一个向量参数。在每个循环中,迭代器变量会从向量中取得一个值。最简单的情况下,该向量只包含整数:

  1. for(i in 1:5) message("i = ", i)
  2. ## i = 1
  3. ## i = 2
  4. ## i = 3
  5. ## i = 4
  6. ## i = 5

如果你想执行多个表达式,与其他循环一样,须使用大括号把它们括起来:

  1. for(i in 1:5)
  2. {
  3. j <- i ^ 2
  4. message("j = ", j)
  5. }
  6. ## j = 1
  7. ## j = 4
  8. ## j = 9
  9. ## j = 16
  10. ## j = 25

R的for循环非常灵活,因为它们的输入并不限于整数或数字,还可以传入字符向量、逻辑向量或列表:

  1. for(month in month.name)
  2. {
  3. message("The month of ", month)
  4. }
  5. ## The month of January
  6. ## The month of February
  7. ## The month of March
  8. ## The month of April
  9. ## The month of May
  10. ## The month of June
  11. ## The month of July
  12. ## The month of August
  13. ## The month of September
  14. ## The month of October
  15. ## The month of November
  16. ## The month of December
  17. for(yn in c(TRUE, FALSE, NA))
  18. {
  19. message("This statement is ", yn)
  20. }
  21. ## This statement is TRUE
  22. ## This statement is FALSE
  23. ## This statement is NA
  24. l <- list(
  25. pi,
  26. LETTERS[1:5],
  27. charToRaw("not as complicated as it looks"),
  28. list(
  29. TRUE
  30. )
  31. )
  32. for(i in l)
  33. {
  34. print(i)
  35. }
  36. ## [1] 3.142
  37. ## [1] "A" "B" "C" "D" "E"
  38. ## [1] 6e 6f 74 20 61 73 20 63 6f 6d 70 6c 69 63 61 74 65 64 20 61 73 20 69
  39. ## [24] 74 20 6c 6f 6f 6b 73
  40. ## [[1]]
  41. ## [1] TRUE

因为for循环操作于向量中的每个元素,所以它提供了一种“伪向量化”。事实上,R的向量化操作通常会在内部的C代码中使用某种形式的for循环。但要注意:R的for循环几乎总是比其对应的向量化运行得要慢,而且往往是一到两个数量级的差别。这意味着你应尽可能地使用向量化2。

2 有公论的是,如果你把R代码编写得和Fortran一样,就没资格抱怨R运行得太慢了。