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)
我自己又把他给的代码全部实现了一遍
实践是最好的老师
推荐阅读
- JAVA(抽象类与接口的区别&重载与重写&内存泄漏)
- 宽容谁
- 我要做大厨
- Docker应用:容器间通信与Mariadb数据库主从复制
- 《真与假的困惑》???|《真与假的困惑》??? ——致良知是一种伟大的力量
- 增长黑客的海盗法则
- 画画吗()
- 2019-02-13——今天谈梦想()
- 远去的风筝
- 第326天