你知道R中的賦值符號箭頭(<-)和等號(=)的區別嗎?

2021-02-20 宏基因組

作為一門高級語言,R語言擁有獨特的語法,比如今天說道的賦值符號。在其他語言裡,賦值符合通常用一個等號(=)表示,而在R語言裡,承擔這個任務的可以是箭頭(<-)符號,也可以是等號(=)。這就導致許多R語言初學者,分不清R語言中的賦值到底是使用箭頭(<-)還是等號(=)?許多早期學習R的童鞋都比較喜歡使用等號(=)進行賦值。畢竟,簡簡單單的a = 5用起來比較符合大多數現有語言的習慣。出於對某種賦值方式的偏好,甚至出現了等號黨和箭頭黨,但是到底孰好孰壞,顯然爭不出任何結果,相對來說更重要的是了解這兩者的區別。只有我們深刻理解了其相同與不同之後,才能更好的運用他們。

R語言最開始設計的時候,是採用箭頭(<-)作為賦值符號的,這是從APL語言繼承而來的(箭頭表示賦值,等號表示判斷)。之後的S語言也沿用了這個用法,再之後R語言為了保持和S語言的兼容性保留了這個箭頭。直到2001年,R的更新版本中 才加入了等號(=)賦值。因此,對於一般的賦值語句,箭頭(<-)與 等號(=)在 功能上是沒有區別的,可以通用。但是等號(=)的作用有兩個:它既可以賦值,也可以傳遞函數參數(實際上傳參可以看作一種特殊形式的賦值,給參數賦值)。通常情況下,如果等號(=)出現在單獨的環境中,它就是賦值;如果寫在函數的參數位置,它就是傳參。如果你在設置參數的時候使用了箭頭(<-),那麼你會發現在全局變量裡,會多出一個和參數名相同的賦值的變量,容易導致歧義和錯誤,而且佔用命名空間。

下面,我們通過幾個個例子來具體講一下這兩個函數的區別。

箭頭(<-)和等號(=)賦值在作用域上的不同。
箭頭(<-)創建的變量的作用範圍為整個全局環境(Global environment),而等號(=)通常在一個局部環境(Local environment)。例如:

> rm(x)  ## 如果變量 x 存在的話,先刪除此變量 > mean(x = 1:10) [1] 5.5 > x Error: object 'x' not found

在以上範例裡,變量 x 是在函數的作用域裡進行聲明的,所以它只存在於此函數中,一旦運算完成便「消失」。

> mean(x <- 1:10) [1] 5.5 > x [1]  1  2  3  4  5  6  7  8  9 10

而採用箭頭(<-)賦值,x 變量則出現在了Global Environment 裡,並且我們可以調用它。 在此例中,實際上是先構建了x變量,再將x傳遞給mean函數的第一個參數,我們看到,採用這種方式,程序也正確運行了,但是採用箭頭(<-)賦值的方式去傳參時要非常小心。可以看下面例子中引起錯誤地情況。

箭頭(<-)和等號(=)在參數傳遞時的區別

> x <- rnorm(100)  # 採用箭頭(<-)進行變量賦值 > y <- 2*x + rnorm(100)  # 採用箭頭(<-)進行變量賦值 > lm(formula=y~x) #上面的代碼完全等價於下面的代碼 > x = rnorm(100)  # 採用等號(=)進行變量賦值 > y = 2*x + rnorm(100)  # 採用等號(=)進行變量賦值 > lm(formula=y~x)

兩段代碼中前兩行都是賦值語句,分別為x變量和y變量賦值,此時等號(=)與箭頭(<-)的功能相同,作用域也相同,因為等號(=)賦值是在全局環境中進行的,而代碼第三行中的等號(=)則是調用函數時規定命名參數,這就是通常情況下,我們直接將y~x這個公式直接傳遞給lm函數的第一個參數,也就是formula參數的用法。如果此時我們將等號(=)替換成箭頭(<-),則會在全局環境中定義出一個新的formula變量,然後再將這個變量傳遞給了lm函數的第一個參數。如果是我們有意這麼做的話,就需要保證命名參數的順序和函數中定義參數的順序相同,否則就會出現錯誤,或者將名稱相同的變量傳遞給了錯誤的參數(但程序可能正常運行),導致結果錯誤。下面的例子可以突出了這種差別:

> 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 = data) #也可以寫成如下形式: > lm(data=data,formula=z~x+y)

當我們將等號(=)替換成箭頭(<-)時,正確的命名參數傳遞應該按函數參數順序來逐個傳參:

> lm(formula <- z~x+y, data <- data) Call:   lm(formula = formula <- z ~ x + y, data = data <- data) Coefficients:   (Intercept)            x            y   0.069869     3.062565     0.007503   > formula z ~ x + y

運行也不會出錯,但是我們會發現函數實際上是調用的lm(formula = formula <- z ~ x + y, data = 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,]    1    5    9 [2,]    2    6   10 [3,]    3    7   11 [4,]    4    8   12 > matrix(c(1:12),ncol<-3)  [,1] [,2] [,3] [,4] [1,]    1    4    7   10 [2,]    2    5    8   11 [3,]    3    6    9   12

我們可以看到,儘管兩種方法,都運行成功,且得到了一個矩陣,但是第二個結果是一個錯誤的結果,此處出錯的原因就是,ncol<-3是將3賦值給變量ncol,然後再傳遞給函數對應位置的參數,而在函數內第二個參數實際上是對應的nrow參數。在實際編寫代碼時,遇到這種情況,如果我們不注意,就會導致後續所有結果都出錯。

此外,還需要注意的一點就是,在傳參中採用箭頭(<-)進行賦值的變量只有在需要使用時才會改變其值。例如:

> a <- 1 > f <- function(x) return(TRUE) > f(a <- a + 1); a [1] TRUE [1] 1

請注意,以上範例裡, a 的值並沒有改變,也就是a並沒有加1,還是原來的a值,這是在函數內部並未用到參數a。這會導致程序裡出現一些不可預期的結果並且降低代碼可讀性,所以不推薦在函數參數裡使用箭頭(<-)這種賦值方式。在看下面的例子:

> a <- 1 > f <- function(x) { +         if(runif(1)>0.5) TRUE +         else print(x) + } > f(a <- a+1);a [1] TRUE [1] 1 > f(a <- a+1);a [1] TRUE [1] 1 > f(a <- a+1);a [1] TRUE [1] 1 > 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 後,只有在隨機數 runif(1) 小於0.5的時候,a 的值才會改變,即執行+1操作,然後列印a。否則傳遞TRUE值。因此,因為隨機數 runif(1) 的隨機性,每次調用函數 f()後 a 的值是不確定的。

現在大家應該清楚了解箭頭(<-)和等號(=)的區別了吧!個人建議,大家寫賦值語句時採用箭頭(<-),傳參時使用等號(=)。這也是大部分老師都會強烈推薦的用法。是因為使用箭頭(<-)賦值,意義清晰,可以保持代碼良好的可讀性,尤其是書寫複雜函數時,避免造成混亂。Google 的 R style guide(https://google.github.io/styleguide/Rguide.xml)也推薦使用箭頭(<-)賦值。 況且有些情況下,只能採用箭頭(<-)賦值,例如:system.time(c<-1:10)中就不能使用等號(=)。而從數學的角度來說,等號兩邊是相等的,即等號左邊的等於等號右邊的,等號右邊的也等於等號左邊的。等號本身並沒有指向性,因此並沒有辦法體現」賦值「這一含義。而在R中,箭頭(<-)符號生動的闡釋了賦值的含義,一個非等號(=)的賦值符從根本上向學習者暗示這樣一個真理: 賦值操作與數學上的等於是完全不同的。此外,箭頭(<-)符號可以雙向賦值,即x <- 10與10 -> x等價。習慣 <- 和 -> 的使用以後,也對後來習慣使用更為複雜的 <<- 以及 ->> 這兩個賦值符號(<<-或->>一般用於函數內部,表示給上一層環境中的變量賦值)做好鋪墊,而 = 無法實現類似的功能。

另外也有等號黨提出異議,認為採用箭頭(<-)不如使用等號(=)。例如:如果我想判斷一個變量是否小於10,可以寫成 x<10;如果我想判斷一個變量是否小於-10,然後順手寫成x<-10,這時候就會產生歧義。關於處理負數時產生歧義的說法,只能說是沒有正確養成良好的空格習慣造成的,句號逗號後加空格,括號外圍加空格,運算符號兩邊加空格,這些應該是學習代碼前就應該懂得的常識。會犯出 a <- 5 和 a < -5 混淆的錯誤只能說明自己的代碼風格糟糕,建議大家Google 的 R style guide(https://google.github.io/styleguide/Rguide.xml )中其他的一些代碼寫作規則。

Reference

https://www.cnblogs.com/loca/p/4301344.html

https://google.github.io/styleguide/Rguide.xml

http://stat.ethz.ch/R-manual/R-patched/library/base/html/assignOps.html

https://stackoverflow.com/questions/1741820/what-are-the-differences-between-and-in-r

http://bbs.pinggu.org/thread-1247151-1-1.html

https://cran.r-project.org/doc/manuals/R-lang.html#Argument-matching

猜你喜歡

10000+:腸道細菌 人體上的生命 寶寶與貓狗 梅毒狂想曲 提DNA發Nature 實驗分析誰對結果影響大  Cell微生物專刊

系列教程:微生物組入門 Biostar 微生物組  宏基因組

專業技能:生信寶典 學術圖表 高分文章 不可或缺的人

一文讀懂:宏基因組 寄生蟲益處 進化樹

必備技能:提問 搜索  Endnote

文獻閱讀 熱心腸 SemanticScholar Geenmedical

擴增子分析:圖表解讀 分析流程 統計繪圖

16S功能預測   PICRUSt  FAPROTAX  Bugbase Tax4Fun

在線工具:16S預測培養基 生信繪圖

科研經驗:雲筆記  雲協作 公眾號

編程模板 Shell  R Perl

生物科普  生命大躍進  細胞暗戰 人體奧秘  

寫在後面

為鼓勵讀者交流、快速解決科研困難,我們建立了「宏基因組」專業討論群,目前己有國內外150+ PI,1300+ 一線科研人員加入。參與討論,獲得專業解答,歡迎分享此文至朋友圈,並掃碼加主編好友帶你入群,務必備註「姓名-單位-研究方向-職稱/年級」。技術問題尋求幫助,首先閱讀《如何優雅的提問》學習解決問題思路,仍末解決群內討論,問題不私聊,幫助同行。

學習16S擴增子、宏基因組科研思路和分析實戰,關注「宏基因組」

點擊閱讀原文,跳轉最新文章目錄閱讀

相關焦點

  • java基礎案例之java語言運算符算術賦值比較邏輯三元和位運算
    "5+5="+5+5);//結果輸出5+5=55.字符串連結這裡55不是五十五,是五五,加號是連接符,不是加法運算//字符串數據,和任何數據使用+(加號)都是相連接,最終都會變成字符串小知識點:轉義符反斜槓 \ ;通過\來轉變後面字母或符號的含義
  • 等號與不等號的由來
    你知道它們的由來嗎?人們是從什麼時候開始使用這些符號的呢?當時,也有人用其他符號表示過相等關係,數學家笛卡兒在1637年出版的《幾何學》中,還曾用「無窮」表示相等關係.17世紀,德國數學家萊布尼茲,在各種場合大力倡導使用符號「=」,在他和其他數學家的共同努力下,這一符號才逐漸被世人所公認.
  • 論文中特殊符號□■∰Σπμλ會輸入嗎?
    論文寫作與編輯中符號和短語的自定義輸入. 學報編輯論叢, 2017:333-338.在論文寫作、繪圖、編輯與編排的過程中,大多數作者通常採用三種方式輸入特殊符號:(1)巧用Word功能處理特殊字符,即採用symbol字體將英文字母顯示為特殊符號。
  • 箭頭符號:一個最常見卻不容忽視的圖標
    輔助表意的下箭頭一個圓形的用戶頭像,右側有一個向下的右箭頭,這是現在用戶界面中一種常見的表現形式,大家都知道點開這個向下的小箭頭你會在用戶頭像下方得到一個關於用戶的菜單。下拉菜單與普通的文本輸入框控制項有什麼區別?只是多了一個箭頭。這裡這些箭頭起著吸引注意力和輔助提示兩個作用,首先文字與圖標的組合形式會比單純的文字連結更能吸引用戶的注意力,並且文字連結的通常用來實現頁面之間的跳轉;其次文字與箭頭圖標的組合形式提示這裡點開還有新的內容。
  • 如何在Word中添加箭頭等框架流程符號
    Word工具製作文本,初入職場的你還在簡單地敲一堆又一堆的字兒就上交領導嗎?下面給大家分享一下,在Word文檔中添加箭頭框架等流程符號的方法,需要的朋友趕緊來看一下吧。1、首先,打開自己創建的Word文檔 ,假如現在想插入一個箭頭。
  • 什麼是R語言
    書中給出了大量的範例代碼,是深入學習實踐R語言的良師益友,對R語言初學小白比較友好。什麼是R語言?我們猜想它是一種程式語言,類似C語言,Visual Basic等。既然已經有那麼多程式語言了,R語言究竟有什麼過人之處呢。我們為什麼要學習R語言?R語言以能夠創建漂亮的圖形而聞名,可以處理各種統計問題。很多強大的功能來自社區開發的數以千計的擴展(包)。
  • 什麼是JavaScript「箭頭函數」?
    ,聽起來好NB,但是如果你知道它是因為使用了=>這樣類似箭頭的符號 ,所以才叫箭頭函數。瞬間感覺:「呵,這名字起的...」。es6增加了箭頭函數的特性當然不是為了裝B,它可以看作是js中函數的一次進化。我們知道,在js中,函數有著不一樣的地位,除了作為普通函數外,它可以用來構造自定義類型,作為對象的方法,通過調用bind方法得到一個新函數。
  • MATLAB符號
    MATLAB的21種特殊符號,總有一種你不知道的用法讀書人的應該不叫偷吧?今天/和\用錯了很是懊惱應該不會有人查到這裡,此乃法外之地本文所指的特殊符號是指除了字母、數字、運算符以外的符號,包括:=;(), []:.{}''""%@~.../\*!?..+。我儘量先從常見的符號說起,非新手可以跳過前面的常見符號。
  • Python圈中的符號計算庫-Sympy
    這種數學符號計算。二、什麼是數學符號計算?數學符號計算能處理表徵數字的符號計算。這意味著數學對象被精確地表示,而不是近似地表示,而具有未被計算的變量的數學表達式被留在符號形式中。sympy的表達式與我們平常的手寫的數學表達式略微有所區別,下面是sympy的方程表示符號加號 +減號 -除號 /乘號 *等號 Eq()指數 **對數 log()e的指數次冪 exp()上面的例子我們用Python實現一下。
  • 乾貨:你知道數組和指針有什麼區別嗎?
    另外,公開寫作則會給你的寫作增加很多維度的外部壓力,你會想如何讓別人更好地理解我要表達的意思;如何傳遞更多價值,讓別人讀完有所收穫;如何讓更多人看到;如何讓別人讀得下去;如何排版讓大家看得更舒服;二、數組和指針有什麼區別?
  • php中六種輸出方式的區別
    php輸出 echo 、 print 、 print_r 、 printf 、 sprintf 、 var_dump 的區別比較。
  • 你知道標識牌上面的箭頭都有什麼作用嗎?
    在我們的生活中我們可以看到不同樣式的標識牌,標識牌上的箭頭也是各式各樣,那麼標識牌上的箭頭都有什麼作用呢?今天就讓前期表示告訴你標識牌上的箭頭都有什麼作用。箭頭被被標識界廣泛運用,主要因素就是因為,箭頭能夠起到導向的作用,為我們指引方向,所以箭頭也成為標識界不能缺少的標識符號,它可以更快地指出方向,讓我們快速地找到目的地,與我們在空間上建立清晰地聯繫,箭頭表示區別於文字標識,就是指引方向更加直觀,避免了複雜的描述。
  • 什麼是JavaScript「箭頭函數」?初學者必看
    ,聽起來好NB,但是如果你知道它是因為使用了=>這樣類似箭頭的符號 ,所以才叫箭頭函數。瞬間感覺:「呵,這名字起的...」。es6增加了箭頭函數的特性當然不是為了裝B,它可以看作是js中函數的一次進化。我們知道,在js中,函數有著不一樣的地位,除了作為普通函數外,它可以用來構造自定義類型,作為對象的方法,通過調用bind方法得到一個新函數。
  • 0、python 的 print()函數、變量和賦值
    雙引號的用法print(''一起玩吧'')在print()函數內不僅能使用單引號,還能使用雙引號,兩者的效果沒什麼區別,都能讓你列印出一行文本。單引號和雙引號可能會在括號內同時出現,如 print(''Let's play'')。在print()函數中,引號裡的內容不定非得是文字,英文和數字都可以。
  • C#中欄位、屬性、只讀、構造函數賦值、反射賦值的相關
    2、欄位和自動屬性的區別是什麼?3、欄位和自動屬性聲明時的直接賦值和構造函數賦值有什麼區別?4、為什麼只讀欄位和只讀自動屬性(只有get沒有set訪問器)都可以在構造函數中進行賦值?5、反射可以給只讀欄位或者只讀屬性進行賦值嗎?6、自動屬性和普通屬性的區別?這些問題是我在試著寫自己的注入實現時遇到的問題。
  • 濤哥講事-新手教程5-C++語句學習之賦值和輸入輸出語句
    最近有點事,濤哥沒有來得及更新教程,今天給大家說一下代碼中的語句。前面大家也看到了,我們的代碼是由很多語句組成的,但是我們寫各種語句都是有規範的,到底有哪些語句,怎麼寫這些語句去解決具體的事情呢。今天我們就先看看兩種最基本的語句:賦值語句和輸入輸出語句。
  • MT4編程入門---指標源碼中的常用符號圖標明細(二)
    回歸正傳,眾所周知MT4指標使用的是MQ語言,今天一起來了解下MT4源碼中的常用符號吧。1、單行注釋「//」2、多行注釋「/* */」程序不執行注釋,注釋內容僅供閱讀或保留不用的語句。在MQ語言編輯器中,注釋內容以灰色顯示。等於號「=」賦值語句,把等號後面的值賦給等號前的變量中。
  • (基礎篇)echo、print、print_r、printf、sprintf、var_dump的區別比較
    注意print總是返回1的,這個和echo不一樣,也就是可以使用print來賦值,不過沒有實際意義。例子:<?php $a = print("55nav"); // 這個是允許的  echo $a; // $a的值是1 ?
  • ARM彙編中^、!、cxsf符號和movs等指令使用
    ,r0=[sp-15*4]@因為沒對pc賦值,所以^的表示將數據恢復到User模式的[r0-lr]寄存器組中[gliethttp] mov r0,r0 add sp,sp,#S_FRAME_SIZE - S_PC movs pc,lr.endm本文引用地址:http://www.eepw.com.cn/article/201611/318785
  • Word裡面這些特殊符號的輸入,你都知道嗎?
    我們在編輯文件的時候,有的時候需要輸入一些特殊符號。有的時候需要輸入一些關於化學的符號,還有些是一些箭頭等。在工具欄那裡找了特殊符號就是沒有找到關於某些我們需要的,特別是化學符號,這就會很麻煩,影響到我們編輯文件的速度和效率。為了避免這種問題出現,我們需要掌握一些關於特殊符號的輸入方法,今天,小編就來和大家分享一下word裡一些特殊符號如何輸入。