今天才發覺R中的& 和 && 不是一回事…git
固然,若是隻計算兩個標量(長度爲1的向量),他倆彷佛是差很少的:github
TRUE & FALSE
## [1] FALSE
TRUE && FALSE
## [1] FALSE
若是計算的是兩個向量的話,結果就明顯不一樣:數組
c(TRUE, FALSE) & c(TRUE, TRUE)
## [1] TRUE FALSE
c(TRUE, FALSE) && c(TRUE, TRUE)
## [1] TRUE
區別在於,& 依次比較兩個向量中的對應元素,而&&只比較兩個向量的首個元素。 &&的這種偷懶的作法確保了它的計算結果只爲一個標量,TURE或FALSE。這就使他 能夠與if等只接受一個標量爲參數的函數完美搭配起來。函數
說到偷懶,和&相比,&&偷懶的地方還不止這一處:spa
a #對象a不存在
## Error in eval(expr, envir, enclos): 找不到對象'a'
FALSE & a
## Error in eval(expr, envir, enclos): 找不到對象'a'
FALSE && a
## [1] FALSE
在進行比較時,&& 若是發現左邊對象的值爲FALSE,那麼他就不會計算右邊的對象了,(由於不管 右邊對象的值爲多少,邏輯與的結果總爲FALSE)因此即便右邊對象不存在時,也沒有拋出錯誤。而 &就老實多了,計算完左邊後他還會計算右邊,而右邊對象不存在,因而拋出錯誤。&&的這種計算方法 叫作短路計算。code
好奇&&是怎麼偷懶的,查看他的源代碼:對象
`&&`
## .Primitive("&&")
不出所料,&&函數是調用編譯好的c代碼。要查看他的源代碼就有點小麻煩,下載未編譯的R源代碼(也能夠直接查看Github上的一個熱心人維持的鏡像),在 src/main/names.c文件中找到這一行:get
{"&&", do_logic2, 1, 0, 2, {PP_BINARY, PREC_AND, 0}},
可知&&在c代碼裏面叫作do_logic2,接下來一番搜索最終在src/main/logic.c中 找到do_logic2的定義,看到其中一行:it
x1 = asLogical(s1);
意思是取得左側對象的值,怎麼取?再看看asLogical這個函數的定義。又是一番搜索 在src/main/coerce.c中找到他的定義,其中幾行:io
switch (TYPEOF(x)) { case LGLSXP: return LOGICAL(x)[0];
注意到[0]中的0,說明只提取數組的第一個元素(c語言中數組下標是從0開始的)。這也就是&&之因此只比較向量的第一個元素的緣由。
回過頭來再接着往下看看do_logic2的定義,看到其中幾行:
case 1: /* && */ if (x1 == FALSE) ans = FALSE; else { get_2nd;
意思很明白,若是&&左邊對象(x1)的值爲FALSE,那麼結果(ans) 就爲FALSE,沒必要再計算右邊對象。不然的話(x1值爲FALSE),還得計算右邊對象(get_2nd)。 這就是&&短路計算的實現方式。分析至此告一段落。
參考:
http://stackoverflow.com/questions/6558921/r-boolean-operators-and
http://stackoverflow.com/questions/19226816/how-can-i-view-the-source-code-for-a-function