表达式与自动打印
R 是一门表达式语言:你写的几乎每一行都会产生一个值。在控制台中,单独输入的表达式会被求值,结果自动打印出来,不需要 print():
每个结果前面都有 [1],这是 R 在告诉你显示的第一个值的位置,也是 R 以向量方式思考的第一个迹象(下面会细说)。自动打印有一个第一天就值得知道的陷阱:它只适用于顶层的单独表达式。同样的表达式放在函数体或 for 循环里,什么都不会打印,所以脚本中需要看到的输出,都要显式地用 cat() 或 print()。
赋值:箭头
R 的赋值运算符是 <-,一个指向接收值的名字的箭头:
radius <- 5 读作“radius 得到 5”。没错,顶层赋值用 = 也可以;也没错,新手都会问为什么有人宁愿多打一个字符。答案是一个有约束力的约定:所有主流风格指南、基础 R 文档,以及你将来读到的几乎所有 R 代码,都用 <- 赋值,而把 = 留给一个特定的用途:在函数调用中给参数命名(见下一节)。遵循这个约定,两种操作在视觉上就能区分开。在 RStudio 中,按 Alt+-(macOS 上是 Option+-)就能输入这个箭头。
注意,赋值不会自动打印,area <- 78.5 只是悄悄地保存这个值。所以上面的代码片段需要 print()。
大小写敏感
R 严格区分大小写,不管是你自己创建的名字,还是语言自带的名字:
这是两个不同的变量。同样,mean() 是一个函数,而 Mean() 根本不存在。当 R 给你报 object 'x' not found 或 could not find function 时,先检查大小写,这是初学者最常犯的错误。
函数调用与具名参数
函数用括号调用,参数之间用逗号分隔。参数可以按位置匹配,也可以按名字匹配,或者两者混用:
seq() 这个调用展示了 R 的具名参数语法,这正是 = 派上用场的地方:在调用内部,name = value 给参数加上标签。具名参数让你不必记住参数的顺序,还能让调用本身就说明自己在做什么;seq(0, 100, 25) 的效果完全一样,但告诉读者的信息更少。实际的约定是:第一个参数(通常是数据)按位置传,其余的都写上名字。
查看任何函数的帮助只需一步:在控制台输入 ?seq,就会打开它的文档。
一切皆向量
让 R 和其他语言感觉不一样的关键思想是:R 中没有单独的数字。就连 5 也是一个长度为一的向量,而运算会对整个向量逐元素进行:
一个循环都没有,但每个元素都被转换了。在大多数语言里,这需要 for 循环迭代三次;在 R 里,这只是算术运算。这就是向量化,它影响着一切:比较运算符、字符串函数和数学函数都是这样工作的。这也是为什么地道的 R 代码中循环比你想象的少得多;完整的介绍见向量。
大括号与代码块
多行的代码体,比如 if、循环和函数的代码体,用大括号 {} 组合在一起:
有两点要注意。条件写在括号里,就是普通的 if,不是 if:,也不是 if ... then。另外,和 Python 不同,R 中的缩进没有任何语法意义:代码块由大括号决定,缩进是给人看的。单行代码省略大括号是合法的,但这是编辑代码时出 bug 的经典来源;干脆一直写上大括号。
空白与分号
R 会忽略多余的空白,换行就结束一条语句,不需要分号:
能跨行书写,是因为第 3 行以 + 结尾,R 知道这个表达式还没结束。这是拆分长行时唯一要记住的规则:在运算符或逗号之后换行,而不要在它们之前换行,否则 R 会认为第一行已经是完整的语句。分号的存在只是为了把两条语句塞进一行(a <- 1; b <- 2):合法,但不推荐,而且很容易避免。
风格:R 代码是怎么写的
除了语法,R 社区还有很强的约定(写在 tidyverse 风格指南中,整个生态的大部分都遵循它):
- 用
<-赋值,=只用于调用中的参数。 snake_case命名:写monthly_revenue,而不是monthlyRevenue或monthly.revenue。R 的名字中可以有点号,但最好避免,因为它和 R 内部使用的一种命名模式冲突。- 运算符两边和逗号后面加空格(写
x <- a * 2,而不是x<-a*2)。 - 大括号内部用四个空格缩进(有些团队用两个,选一种并保持一致)。
- 每行一条语句。
这些都不是解释器强制要求的,但所有读你代码的人都会这么要求你。关于命名的部分,会在变量中继续讲。
要点回顾
- R 会对表达式求值,并在控制台中自动打印单独的表达式,但在函数或循环内部不会。
- 用
<-(“得到”)赋值;=只用于在函数调用中给参数命名。 - R 完全区分大小写,早期遇到的大多数 “not found” 错误都是大小写写错了。
- 一切皆向量,运算逐元素进行:
c(1, 2, 3) * 2就是2 4 6,不需要循环。 - 大括号定义代码块,换行结束语句,
snake_case加上空格约定,能让你的代码读起来和其他人的一样。
下一篇:注释,如何给 R 代码加注释,以及 R 没有块注释语法时该怎么办。
常见问题
R 中的 <- 是什么意思?
<- 是 R 的赋值运算符:x <- 5 把值 5 存到名字 x 下面。可以读作“得到”。在顶层,= 也能赋值,但社区的约定(所有主流风格指南都这样要求)是用 <- 赋值,= 只用于在函数调用中给参数命名。
R 区分大小写吗?
区分,而且完全区分。total、Total 和 TOTAL 是三个不同的名字,把 mean(x) 写成 Mean(x) 会直接报错,而不只是警告。“could not find function”或“object not found”错误,很多时候只是大小写写错了。
R 需要分号吗?
不需要。换行就结束了一条语句。只有想把两条语句挤在同一行时(a <- 1; b <- 2)才需要分号,而风格指南本来也不建议这样做。每行写一条语句,就再也不用操心分号了。
R 是向量化的,这是什么意思?
R 的基本运算会一次作用于整个向量。c(1, 2, 3) * 2 的结果是 2 4 6,乘法作用在每个元素上,不需要循环。在其他语言里要写成 for 循环的代码,在 R 中大多只是一个表达式。