R 是一种过程式编程语言。因此,它拥有与其他许多语言一样的完整流程控制语法集。实际上,R 语言中的流程控制语法与 Java 和 C 相似。在这篇文章中,你将看到一些在 R 语言中使用流程控制语法的例子。
让我们开始吧。

R 中的逻辑、流程控制和函数
图片由 Cris DiNoto 拍摄。保留部分权利。
概述
这篇文章分为三个部分;它们是
- 寻找素数
- 埃拉托斯特尼筛法
- 最长连续素数之和
寻找素数
让我们从一个简单的问题开始:找出小于某个数 N 的所有素数列表。
第一个素数是 2。任何大于 2 的整数,如果不能被任何小于它的素数整除,那么它就是素数。这是一个简单的定义。我们可以将其转换为 R 程序,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
# 找出小于某个数的所有素数 pmax <- 1000 # 寻找素数的上限 # 初始化一个向量来存储素数 primes <- c() # 遍历所有整数 for (i in 2:pmax) { # 检查整数是否能被已找到的任何素数整除 isPrime <- TRUE for (j in primes) { if (i %% j == 0) { isPrime <- FALSE break } } # 如果整数是素数,则将其添加到 primes 向量中 if (isPrime) { primes <- c(primes, i) } } # 打印素数 print(primes) |
如果你能成功运行,你将看到以下输出
1 2 3 4 5 6 7 8 9 10 |
[1] 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 [19] 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 139 149 151 [37] 157 163 167 173 179 181 191 193 197 199 211 223 227 229 233 239 241 251 [55] 257 263 269 271 277 281 283 293 307 311 313 317 331 337 347 349 353 359 [73] 367 373 379 383 389 397 401 409 419 421 431 433 439 443 449 457 461 463 [91] 467 479 487 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 [109] 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 683 691 701 [127] 709 719 727 733 739 743 751 757 761 769 773 787 797 809 811 821 823 827 [145] 829 839 853 857 859 863 877 881 883 887 907 911 919 929 937 941 947 953 [163] 967 971 977 983 991 997 |
上述代码的算法如下:你从 2 扫描到 `pmax`(包括两端),对于每个数字 `i`,你使用另一个 for 循环来检查任何现有素数 `j` 是否能整除当前数字。如果 `i %% j == 0`,你就知道 `i` 不是素数。因此你将 `isPrime` 标记为 `FALSE` 并停止。
在每次迭代结束时,素数会被添加到 `prime` 向量中。当程序结束时,该向量将包含上限以下的所有素数。
从上面可以看出 R 语言的一些基本特性。R 中的条件分支语法如下:
1 2 3 4 5 |
if (expression) { statement1 } else { statement2 } |
这种语法类似于 JavaScript,尽管用于标记每个语句末尾的分号是可选的。
条件应该是布尔值。因此,我们可以使用上面的逻辑变量 `isPrime`,或者比较语句 `i %% j == 0`。运算符 `%%` 用于取模除法。你可以找到常见的 R 运算符及其优先级表如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
:: ::: 访问 命名空间中的 变量 $ @ 组件 / 槽 提取 [ [[ 索引 ^ 幂运算 (从右 到 左) - + 一元 减 和 加 : 序列 运算符 %任意% |> 特殊 运算符 (包括 %% 和 %/%) * / 乘, 除 + - (二元) 加, 减 < > <= >= == != 排序 和 比较 ! 否定 & && 与 | || 或 ~ 如 公式 中 -> ->> 右向 赋值 <- <<- 赋值 (从右 到 左) = 赋值 (从右 到 左) ? 帮助 (一元 和 二元) |
你可以在 R 中使用帮助语句“`?Syntax`”(“Syntax”大写 S)找到此表。
在 C 和 Java 中,你可能会记得有一个三元运算符“`condition?value_true:value_false`”。这是一个运算符,因为它的使用仅限于根据条件的真值返回一个值(`value_true` 或 `value_false`),而不是执行一大段代码。R 中也有类似的功能,作为一个函数:
1 |
ifelse(condition, value.true, value.false) |
但你不应该将其与 if-else 语句混淆。
此外,你可以使用与 C 或 Java 类似的嵌套 if 语法
1 2 3 4 5 |
if (condition) { statement1 } else if (condition) { statement2 } |
然而,R 中没有 `switch` 语句。相反,`switch()` 是一个函数,其语法如下所示:
1 2 3 |
y <- "fruit" letseat <- switch(y, fruit="apple", veg="broccoli", "nothing") cat(sprintf("让我们吃 %s\n", letseat)) |
在前面的例子中,你还看到了如何在 R 中创建 for 循环:你需要提供一个向量,循环将逐个扫描向量元素。for 循环不要求迭代整数,上面的代码只是一个例子。
当你处于循环中时,你总是可以使用 `break` 语句提前终止循环,或者使用 `next` 语句提前开始下一次迭代。另一个例子如下。
埃拉托斯特尼筛法
如果你将上限设置为更高的值(例如,一百万),则前面寻找素数的例子会很慢。更快的算法是埃拉托斯特尼筛法,代价是会占用略多一些的内存。其思想是每次找到一个素数,然后将它的所有倍数从素数候选列表中排除。
R 中埃拉托斯特尼筛法的实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
# 使用埃拉托斯特尼筛法查找素数 # 创建一个全部为 TRUE 的向量 pmax <- 1000 primality <- rep(TRUE, pmax) # 运行筛法 primality[1] <- FALSE for (i in 1:pmax) { if (!primality[i]) { next } if (i*i > pmax) { break } for (j in seq(i*i, pmax, by=i)) { primality[j] <- FALSE } } # 查找值为 TRUE 的索引 primes <- which(primality) print(primes) |
这段代码应该产生与前一个代码相同的输出。
在上面的代码中,你看到了如何使用 `next` 和 `break` 语句来控制 for 循环的流程。你还可以看到如何使用 `rep()` 函数创建具有相同值(`TRUE`)的向量,以及如何使用 `seq()` 函数创建从 `i*i` 到 `pmax` 均匀间隔的向量。
在代码的最后,你使用 `which()` 函数来查找向量值为 `TRUE` 的索引。在 R 中,向量是从 1 开始索引的。因此,`primality` 向量在 for 循环开始前被创建,并将第一个元素设置为 `FALSE`(因为 1 不被认为是素数)。
R 中有许多内置函数。上面的代码向你展示了一些,你可以从“R 参考卡片”中学习一些最常用的函数。
最长连续素数之和
如上编写程序对许多项目都很有用,但是当你遇到一个更大的问题时,你可能需要一种将程序组织成功能块的方法。R 不仅支持内置函数,还允许你创建自己的函数。
让我们考虑一个稍大的程序。这是来自 Project Euler 的问题 50。你想要找出一百万以下,且由最长连续素数之和组成的素数。例如,前 6 个素数之和是 2+3+5+7+11+13=41,而 41 是一个素数。答案是 997651,它是 543 个素数之和。
既然你有一种方法可以生成达到一百万的素数,你可以扫描素数向量并求和,然后验证该和是否也是素数,直到该和小于一百万。同时,你需要跟踪符合条件的最长素数之和。
以下是如何在 R 中解决这个问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# 欧拉计划 #50 # 返回一个上限内的素数向量 getprimes <- function(pmax) { primality <- rep(TRUE, pmax) primality[1] <- FALSE # 运行埃拉托斯特尼筛法 for (i in 1:pmax) { if (!primality[i]) { next } if (i*i > pmax) { break } for (j in seq(i*i, pmax, by=i)) { primality[j] <- FALSE } } # 返回值为 TRUE 的索引 return(which(primality)) } # 找出最长的素数之和 pmax <- 1000000 primes <- getprimes(pmax) count_max = 0 ans <- -1 for (i in 1:(length(primes)-1)) { sum <- primes[i] count <- 1 for (j in i+1:length(primes)) { sum <- sum + primes[j] count <- count + 1 if (sum > pmax) { break } if ((sum %in% primes) && (count > count_max)) { ans <- primes[i:j] count_max <- count } } } print(ans) print(length(ans)) print(sum(ans)) |
你可以看到,一个自定义函数被创建来返回所有素数的列表。函数使用 `function()` 语法和 `return()` 定义。当你调用函数,例如 `primes <- getprimes(pmax)` 时,`return()` 返回的任何值都会被赋给该变量。
上述代码的其余部分应该对你来说很熟悉:它们是由 for 循环和 if 语句构建的。你应该还会看到答案是如何在循环中记录和更新的。
你应该注意一个微妙的问题:在 `i` 的 for 循环中,它一直到 `length(primes)-1`,而 `j` 的 for 循环从 `i+1` 开始。这是为了确保我们正确计算和,因为在 R 中,可以使用 5:2 或 5:5 这样的语法创建向量,它们分别是一个降序序列和一个单元素向量。
如果你正确运行代码,你应该会看到以下输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
[1] 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 [16] 67 71 73 79 83 89 97 101 103 107 109 113 127 131 137 [31] 139 149 151 157 163 167 173 179 181 191 193 197 199 211 223 [46] 227 229 233 239 241 251 257 263 269 271 277 281 283 293 307 [61] 311 313 317 331 337 347 349 353 359 367 373 379 383 389 397 [76] 401 409 419 421 431 433 439 443 449 457 461 463 467 479 487 [91] 491 499 503 509 521 523 541 547 557 563 569 571 577 587 593 [106] 599 601 607 613 617 619 631 641 643 647 653 659 661 673 677 [121] 683 691 701 709 719 727 733 739 743 751 757 761 769 773 787 [136] 797 809 811 821 823 827 829 839 853 857 859 863 877 881 883 [151] 887 907 911 919 929 937 941 947 953 967 971 977 983 991 997 [166] 1009 1013 1019 1021 1031 1033 1039 1049 1051 1061 1063 1069 1087 1091 1093 [181] 1097 1103 1109 1117 1123 1129 1151 1153 1163 1171 1181 1187 1193 1201 1213 [196] 1217 1223 1229 1231 1237 1249 1259 1277 1279 1283 1289 1291 1297 1301 1303 [211] 1307 1319 1321 1327 1361 1367 1373 1381 1399 1409 1423 1427 1429 1433 1439 [226] 1447 1451 1453 1459 1471 1481 1483 1487 1489 1493 1499 1511 1523 1531 1543 [241] 1549 1553 1559 1567 1571 1579 1583 1597 1601 1607 1609 1613 1619 1621 1627 [256] 1637 1657 1663 1667 1669 1693 1697 1699 1709 1721 1723 1733 1741 1747 1753 [271] 1759 1777 1783 1787 1789 1801 1811 1823 1831 1847 1861 1867 1871 1873 1877 [286] 1879 1889 1901 1907 1913 1931 1933 1949 1951 1973 1979 1987 1993 1997 1999 [301] 2003 2011 2017 2027 2029 2039 2053 2063 2069 2081 2083 2087 2089 2099 2111 [316] 2113 2129 2131 2137 2141 2143 2153 2161 2179 2203 2207 2213 2221 2237 2239 [331] 2243 2251 2267 2269 2273 2281 2287 2293 2297 2309 2311 2333 2339 2341 2347 [346] 2351 2357 2371 2377 2381 2383 2389 2393 2399 2411 2417 2423 2437 2441 2447 [361] 2459 2467 2473 2477 2503 2521 2531 2539 2543 2549 2551 2557 2579 2591 2593 [376] 2609 2617 2621 2633 2647 2657 2659 2663 2671 2677 2683 2687 2689 2693 2699 [391] 2707 2711 2713 2719 2729 2731 2741 2749 2753 2767 2777 2789 2791 2797 2801 [406] 2803 2819 2833 2837 2843 2851 2857 2861 2879 2887 2897 2903 2909 2917 2927 [421] 2939 2953 2957 2963 2969 2971 2999 3001 3011 3019 3023 3037 3041 3049 3061 [436] 3067 3079 3083 3089 3109 3119 3121 3137 3163 3167 3169 3181 3187 3191 3203 [451] 3209 3217 3221 3229 3251 3253 3257 3259 3271 3299 3301 3307 3313 3319 3323 [466] 3329 3331 3343 3347 3359 3361 3371 3373 3389 3391 3407 3413 3433 3449 3457 [481] 3461 3463 3467 3469 3491 3499 3511 3517 3527 3529 3533 3539 3541 3547 3557 [496] 3559 3571 3581 3583 3593 3607 3613 3617 3623 3631 3637 3643 3659 3671 3673 [511] 3677 3691 3697 3701 3709 3719 3727 3733 3739 3761 3767 3769 3779 3793 3797 [526] 3803 3821 3823 3833 3847 3851 3853 3863 3877 3881 3889 3907 3911 3917 3919 [541] 3923 3929 3931 [1] 543 [1] 997651 |
这告诉你 997651 是 543 个素数之和。
进一步阅读
您可以从以下来源了解有关上述主题的更多信息:
网站
- R 手册:https://cran.r-project.cn/manuals.html
- R 参考卡片(PDF):https://cran.r-project.cn/doc/contrib/Short-refcard.pdf
- 欧拉计划:https://projecteuler.net/
书籍
总结
在这篇文章中,你通过示例学习了一些 R 编程语法以及如何定义自己的 R 函数。具体来说,你学习了:
- 如何创建循环和分支
- 如何使用 next 和 break 控制循环中的流程
- 如何创建和使用自定义函数
暂无评论。