R|R <-与=

【R|R <-与=】了解两者的区别
R最开始设计的时候是以<-作为赋值符号的,这是从APL语言继承而来的(箭头表示赋值,等号表示判断)。之后S语言也沿用了这个用法。
对于一般的赋值语句<- 与=在功能上没有区别,可以通用。
但是=的作用有两个:它既可以赋值,也可以传递函数参数()
通常情况下=出现在单独的环境中,他就是赋值;如果写在函数的参数位置,他就是传参
如果你在设置参数的时候使用了<-,那么你会发现在全局变量里,会多出一个和参数名相同的赋值的变量,容易导致歧义和错误,而且占用命名空间
1 <- 和= 赋值在作用域上不同 <- 创建的变量作用范围为整个全局环境,而=通常在一个局部环境

> rm(list=ls())## 清空所有变量 >mean(x = 1:10) [1] 5.5 >x Error: object 'x' not found

以上范例里,x是在函数的作用域里声明的,它只存在于此函数中,一旦完成运算就消失了。
> rm(list = ls())## 清空所有变量 >mean(x <- 1:10) [1] 5.5 >x [1]123456789 10

使用<-赋值,变量出现在了global environment里,并且我们还可以调用它。
实际上是先构造了x变量,再将x传递给mean函数的第一个参数。
但是采用<-赋值的方式去传参要非常小心,看例子二这个错误
2.<-和=在参数传递时的区别
rm(list = ls())## 清空所有变量 >x <- rnorm(100)# 采用箭头(<-)进行变量赋值 >y <- 2*x + rnorm(100)# 采用箭头(<-)进行变量赋值 > lm(formula=y~x)Call: lm(formula = y ~ x)Coefficients: (Intercept)x 0.010761.92460 #上面的代码完全等价于下面的代码 >rm(list = ls())#清空所有变量 >x = rnorm(100)# 采用等号(=)进行变量赋值 >y = 2*x + rnorm(100)# 采用等号(=)进行变量赋值 >lm(formula=y~x)Call: lm(formula = y ~ x)Coefficients: (Intercept)x -0.0038941.850902

两段代码中前两行公式赋值语句,分别为x变量和y变量赋值,此时= 和<-功能相同,作用域也相同
因为前两行=的赋值是在全局变量的环境下进行的
而代码第三行中的=则是调用函数时规定命名参数,这就是通常情况下,我们直接将y~x这个公式直接传递给lm函数的第一个参数,也就是formula参数的用法
如果此时我们将=替换成<-,则会在全局环境中定义出一个新的formula变量,然后再将这个变量传递给了lm函数的第一个参数。如果我们这么做了,要注意 保证命名参数的顺序和函数中定义参数的顺序要相同 否则就会报错,但也可能将名称相同的变量传递给了错误的参数(但程序可能正常运行),但结果错误,下面举个例子突出这种差别
rm(list = ls())#清空所有变量 x <- rnorm(100) y <- 2*x+rnorm(100) z <- 3*x+rnorm(100) data <- data.frame(z,x,y) rm(x, y, z)此时,环境中已经没有x,y,z变量,就只有变量data可以用来做z~x+y的线性回归。标准写法: lm(formula=z~x+y,data = https://www.it610.com/article/data) #也可以写成如下形式: #lm(data=data,formula=z~x+y)> rm(list = ls())#清空所有变量 >x <- rnorm(100) >y <- 2*x+rnorm(100) >z <- 3*x+rnorm(100) >data <- data.frame(z,x,y) >rm(x, y, z) >lm(formula=z~x+y,data = https://www.it610.com/article/data)Call: lm(formula = z ~ x + y, data = data)Coefficients: (Intercept)xy -0.063732.862000.08099

当我们将=替换成<-时,正确的命名参数传递应该按函数参数顺序来逐个传参
> lm(formula <- z~x+y, data <- data)Call: lm(formula = formula <- z ~ x + y, data = https://www.it610.com/article/data <- data)Coefficients: (Intercept)xy -0.063732.862000.08099>formula z ~ x + y

运行也没有出错,但是我们可以看到在第二个运行界面函数实际上是调用的lm(formula=formula<- z~x+y,data<-data)
这时产生了一个新的变量formula到环境中,并且在全局环境中就可以使用(实际上data变量也被更新了)
但是如果我们对于lm函数的参数顺序不了解或由于马虎搞错参数顺序,这个时候就会容易出现问题。
> #错误的写法: >lm(data <- data,formula <- z~x+y) Error in as.data.frame.default(data) : cannot coerce class ‘"formula"’ to a data.frame

报错,他说参数类型不匹配而导致异常。因为data被当作第一个参数formula传递,而formula被当作第二个参数data传递,造成了参数类型不匹配。
因此函数的命名参数传递,尽量不要使用<-,因为既会产生副作用(创建新变量),也无法利用命名参数传递的功能
有时程序也不一定能发现错误,例如我们在构建矩阵时
> # 构建一个3列的矩阵 >matrix(c(1:12),ncol=3) [,1] [,2] [,3] [1,]159 [2,]2610 [3,]3711 [4,]4812 >matrix(c(1:12),ncol<-3) [,1] [,2] [,3] [,4] [1,]14710 [2,]25811 [3,]36912

我们可以看到,尽管两种方法都运行成功,且得到一个矩阵,但是第二个结果是一个错误的结果,此处错误的原因是,ncol<-3是将3赋值给了变量ncol,然后再传递给函数对应位置的参数,而在函数内的第二个参数实际上是对应的nrow参数。在实际编写代码时,这种情况要仔细排查。
所以对于参数传递来讲赋值最好用=
3还需注意一点,在传参中采用<-进行赋值的变量只有在需要使用时才会改变其的值,例如:
> a <- 1 >f <- function(x) return(TRUE) >f(a <- a + 1); a [1] TRUE [1] 1

这个例子中a的值没有改变,也就是a并没有+1,还是原来的a值,这是在函数内部并没有使用到参数a
所以不推荐在函数参数里使用(<-)这种赋值方式
再看个例子
> rm(list = ls())#清除所有变量 >a <- 1 >f <- function(x) { +if(runif(1)>0.5) TRUE +else print(x) +} >f(a <- a+1); a [1] 2 [1] 2 >f(a <- a+1); a [1] TRUE [1] 2 >f(a <- a+1); a [1] 3 [1] 3 >f(a <- a+1); a [1] TRUE [1] 3 >f(a <- a+1); a [1] TRUE [1] 3 >f(a <- a+1); a [1] 4 [1] 4

上述代码中,向函数 f() 传递传递参数 a <- a + 1 后,只有在随机数 runif(1) 小于0.5的时候,a 的值才会改变,即执行+1操作,然后打印a。否则传递TRUE值。因此,因为随机数 runif(1) 的随机性,每次调用函数 f()后 a 的值是不确定的。
大家写赋值语句时采用箭头(<-),传参时使用等号(=)。 注1:有些情况下,只能采用箭头(<-)赋值,例如:system.time(c<-1:10)中就不能使用等号(=)。
此外,箭头(<-)符号可以双向赋值,即x <- 10与10 -> x等价。习惯 <- 和 -> 的使用以后,也对后来习惯使用更为复杂的 <<- 以及 ->> 这两个赋值符号(<<-或->>一般用于函数内部,表示给上一层环境中的变量赋值)做好铺垫,而 = 无法实现类似的功能。
注2:
a<- 5 #把5赋值给a a< -5 #判断变量a是否小于-5

Reference:
阅读学习 陈亮 [宏基因组] 的推文
(https://mp.weixin.qq.com/s/DTRrv5JqQJlZGNsi4cudNw)
我自己又把他给的代码全部实现了一遍
实践是最好的老师

    推荐阅读