R 內部結構

這是 R 內部結構的指南,以及從事 R 本身工作的核心團隊的編碼標準。

本手冊適用於 R 4.3.3 版(2024-02-29)。

版權所有 © 1999–2023 R Core Team

只要所有副本都保留版權聲明和此許可聲明,即授予製作和散發本手冊逐字副本的許可。

只要整個衍生作品都是根據與本許可聲明相同的條款散發的,即授予在逐字複製條件下複製和散發本手冊修改版本的許可。

在上述修改版本條件下,允許複製和散布本手冊的翻譯版本,但此許可通知可以陳述在 R 核心團隊核准的翻譯版本中。

目錄


1 R 內部結構

本章節是 R 內部結構文件檔的開頭。它寫給核心團隊和其他研究 src/main 目錄中程式碼的人。

這是一個進行中的工作,應該與目前版本的原始程式碼進行比對。R 2.x.y 版本包含功能何時導入的歷史註解:這個版本是針對 3.x.y 系列。


1.1 SEXP

R 使用者認為的變數物件是繫結到值的符號。該值可以視為SEXP(指標)或其指向的結構SEXPREC(且有其他形式用於向量,即指向VECTOR_SEXPREC結構的VECSXP)。因此,R 物件的基本建構區塊通常稱為節點,意指SEXPRECVECTOR_SEXPREC

請注意,SEXPREC的內部結構並未公開給 R 擴充套件:SEXP反而是個不透明指標,且內部結構只能透過提供的函式存取。

兩種節點結構的前三個欄位都是 64 位元sxpinfo標頭,然後是三個指標(指向屬性、前一個節點和雙向連結清單中的下一個節點),然後是一些其他欄位。在 32 位元平台上,節點1佔用 32 個位元組:在 64 位元平台上,通常為 56 個位元組(視對齊限制而定)。

sxpinfo 標頭的前 5 個位元指定多達 32 個 SEXPTYPE 之一。


1.1.1 SEXPTYPE

目前使用中的 SEXPTYPE 為 0:10 和 13:25。值 11 和 12 用於內部因子和已排序因子,且自此已取消使用。請注意,SEXPTYPE 數字儲存在已儲存的物件中,且會使用類型的順序,因此無法輕易重複使用間隙。

SEXPTYPE說明
0NILSXPNULL
1SYMSXP符號
2LISTSXP配對清單
3CLOSXP封閉
4ENVSXP環境
5PROMSXP承諾
6LANGSXP語言物件
7SPECIALSXP特殊函數
8BUILTINSXP內建函數
9CHARSXP內部字元字串
10LGLSXP邏輯向量
13INTSXP整數向量
14REALSXP數值向量
15CPLXSXP複數向量
16STRSXP字元向量
17DOTSXP點點點物件
18ANYSXP讓「任何」引數運作
19VECSXP清單(一般向量)
20EXPRSXP表達式向量
21BCODESXP位元組碼
22EXTPTRSXP外部指標
23WEAKREFSXP弱參考
24RAWSXP原始向量
25S4SXPS4 類別不是簡單類型

其中許多將會從 R 層級熟悉:原子向量類型為 LGLSXPINTSXPREALSXPCPLXSPSTRSXPRAWSXP。清單為 VECSXP,名稱(也稱為符號)為 SYMSXP。配對清單(LISTSXP,名稱可追溯至 R 作為類似 Scheme 語言的起源)在 R 層級很少見,但例如用於引數清單。字元向量實際上是清單,其所有元素都是 CHARSXP,這種類型在 R 層級很少見。

語言物件(LANGSXP)是呼叫(包括公式等)。在內部,它們是配對清單,第一個元素是對要呼叫函式的參考2,其餘元素是呼叫的實際引數(如果存在標籤,則提供指定的引數名稱)。雖然這並未強制執行,但程式碼中的許多地方假設配對清單長度為一或更多,通常不檢查。

表達式為 EXPRSXP 類型:它們是(通常為語言)物件的向量,最常視為 parse() 的結果。

函式為 CLOSXPSPECIALSXPBUILTINSXP 類型:其中 SEXPTYPE 儲存在整數中,有時會將它們合併到偽類型 FUNSXP 中,代碼為 99。透過 function 定義的函式為 CLOSXP 類型,並具有形式、主體和環境。

SEXPTYPE S4SXP 用於 S4 物件,這些物件不完全由簡單類型組成,例如原子向量或函式。


1.1.2 標頭的其餘部分

請注意標頭的大小和結構在 R 3.5.0 中已變更:請參閱本手冊的較早版本以取得先前的配置。

sxpinfo 標頭定義為 64 位元 C 結構

#define NAMED_BITS 16
struct sxpinfo_struct {
    SEXPTYPE type      :  5;  /* discussed above */
    unsigned int scalar:  1;  /* is this a numeric vector of length 1?
    unsigned int obj   :  1;  /* is this an object with a class attribute? */
    unsigned int alt   :  1;  /* is this an ALTREP object? */
    unsigned int gp    : 16;  /* general purpose, see below */
    unsigned int mark  :  1;  /* mark object as ‘in use’ in GC */
    unsigned int debug :  1;
    unsigned int trace :  1;
    unsigned int spare :  1;  /* debug once and with reference counting */
    unsigned int gcgen :  1;  /* generation for GC */
    unsigned int gccls :  3;  /* class of node for GC */
    unsigned int named : NAMED_BITS; /* used to control copying */
    unsigned int extra : 32 - NAMED_BITS;
}; /*		    Tot: 64 */

debug 位元用於封閉元和環境。對於封閉元,它是由 debug() 設定,並由 undebug() 取消設定,並表示函式的評估應在瀏覽器下執行。對於環境,它表示瀏覽是否處於單步模式。

trace 位元用於 trace() 的函式,以及用於追蹤重複時的其他物件 (請參閱 tracemem)。

spare 位元用於封閉元,以將它們標記為一次性除錯。

named 欄位由 SET_NAMEDNAMED 巨集設定和存取,並採用值 012,或者如果 NAMEDMAX 設定為較高的值,則可能更高。R 具有「依值呼叫」的錯覺,因此類似這樣的指定

b <- a

[NAMED 機制已由參考計數取代。]

似乎會複製 a 的副本,並將其稱為 b。但是,如果 ab 都沒有隨後被變更,則不需要複製。實際上發生的是,新的符號 ba 繫結到相同的值,並設定值物件上的 named 欄位(在此情況下為 2)。當準備變更物件時,會諮詢 named 欄位。值 2 或以上表示必須在變更之前複製物件。(請注意,這並非表示有必要複製,僅表示無論是否必要都應該複製。)值 0 表示已知沒有其他 SEXP 與此物件共用資料,因此可以安全地變更。值 1 用於以下情況:

dim(a) <- c(7, 2)

在原則上,計算期間存在 a 的兩個副本,例如(原則上)

a <- `dim<-`(a, c(7, 2))

但時間不會更長,因此某些原始函數可以在這種情況下進行最佳化,以避免複製。[此機制預計在 R 4.0.0 中取代。]

根據定義,gp 位元是「一般用途」。我們將它們標示為 0 到 15。位元 0-5 和位元 14-15 已如下所述使用(主要是從來源偵查工作)。

位元可以由 LEVELSSETLEVELS 巨集存取和設定,這些名稱似乎可以追溯到內部因子和已排序類型,現在只在程式碼中的幾個地方使用。除了 NILSXPSYMSXPENVSXP 之外的 SEXPTYPE 會序列化/解除序列化 gp 欄位。

gp 的位元 14 和 15 用於「特殊繫結」。位元 14 用於鎖定繫結或環境,而位元 15 用於指出一個活動繫結。(有關「活動繫結」的定義,請參閱檔案 src/main/envir.c 中的標頭註解。)位元 15 用於環境,以指出它是否參與全域快取。

巨集 ARGUSEDSET_ARGUSED 在比對實際和形式函式引數時使用,並採用值 0、1 和 2。

巨集 MISSINGSET_MISSING 用於引數的配對清單。保留四個位元,但只使用兩個位元(而且沒有明確說明用途)。位元 0 似乎由 matchArgs_NR 使用,以標記傳回引數清單中的遺失,而位元 1 則用於標記將預設值用於複製到封閉評估架構的引數。

位元 0 由巨集 DDVALSET_DDVAL 使用。這表示一個 SYMSXP 是符號 ..n 之一,這些符號在處理 ... 時會隱含建立,因此表示可能需要在 DOTSXP 中查詢它。

位元 0 用於 PRSEEN,一個旗標用於指出一個承諾在評估承諾時是否已經看過(因此可以避免遞迴迴圈)。

位元 0 用於 HASHASH,在環境架構的 TAGPRINTNAME 上。(這個位元不會序列化為 CHARSXP 物件。)

位元 0 和 1 用於弱參考(用於指出「準備完成」、「在結束時完成」)。

位元 0 由條件處理系統(在 VECSXP 上)使用,以指出呼叫處理常式。

位元 4 會開啟以標記 S4 物件。

位元 1、2、3、5 和 6 用於 CHARSXP,以表示其編碼。位元 1 表示 CHARSXP 應視為一組位元組,不一定要表示任何已知編碼中的字元。位元 2、3 和 6 分別用於表示已知為 Latin-1、UTF-8 或 ASCII

對於 CHARSXP,位元 5 表示其已依其位址雜湊,亦即 NA_STRING 或在 CHARSXP 快取中(這未序列化)。只有在例外情況下,CHARSXP 才不會雜湊,而這不應在終端使用者程式碼中發生。


1.1.3 ‘資料’

SEXPREC 是 C 結構,包含如上所述的 64 位元標頭、三個指標(指向屬性、前一個和下一個節點)和節點資料(一個聯合)

union {
    struct primsxp_struct primsxp;
    struct symsxp_struct symsxp;
    struct listsxp_struct listsxp;
    struct envsxp_struct envsxp;
    struct closxp_struct closxp;
    struct promsxp_struct promsxp;
} u;

除了第一個(int)之外,所有這些替代方案都是三個指標,因此聯合佔用三個字詞。

向量類型為 RAWSXPCHARSXPLGLSXPINTSXPREALSXPCPLXSXPSTRSXPVECSXPEXPRSXPWEAKREFSXP。請記住,這些類型為 VECTOR_SEXPREC,它再次包含標頭和相同的三個指標,但後跟兩個整數,給出向量的長度和「真實長度」3,然後後跟資料(依需要對齊:在具有 24 位元組 VECTOR_SEXPREC 節點的大多數 32 位元組系統上,資料可以在節點之後立即跟隨)。資料是一個適當長度的記憶體區塊,用於儲存「真實長度」元素(向上捨入為 8 位元組的倍數,8 位元組區塊為 gc() 文件中提到的「Vcell」)。

各種類型的「資料」在下面的表格中給出。其中很多是解釋,即不檢查類型。

NILSXP

只有一個 NILSXP 類型的物件,R_NilValue,沒有資料。

SYMSXP

指向三個節點的指標,名稱、值和內部,由 PRINTNAMECHARSXP)、SYMVALUEINTERNAL 存取。(如果符號的值是 .Internal 函數,最後一個是指向適當 SEXPREC 的指標。)許多符號具有 SYMVALUE R_UnboundValue

LISTSXP

指向 CAR、CDR(通常為 LISTSXPNULL)和 TAG(SYMSXPNULL)的指標。

CLOSXP

指向形式(配對清單)、主體和環境的指標。

ENVSXP

指向框架、封閉環境和雜湊表(NULLVECSXP)的指標。框架是一個標籤對列表,標籤為符號,CAR 為綁定值。

PROMSXP

指向值、表達式和環境(用於評估表達式)的指標。一旦承諾已評估,環境將設定為 NULL

LANGSXP

一種特殊的 LISTSXP 類型,用於函數呼叫。(CAR 參考函數(可能透過符號或語言物件),CDR 參考帶有命名參數標籤的參數清單。)R 層級文件對「表達式」/「語言物件」的參考主要是 LANGSXP,但可以是符號(SYMSXP)或表達式向量(EXPRSXP)。

SPECIALSXP
BUILTINSXP

一個整數,給出基本類型/.Internal 表格中的偏移量。

CHARSXP

lengthtruelength,後接一個位元組區塊(允許 nul 終止符)。

LGLSXP
INTSXP

lengthtruelength,後接一個 C int 區塊(在所有 R 平台上都是 32 位元)。

REALSXP

lengthtruelength,後接一個 C double 區塊。

CPLXSXP

lengthtruelength,後接一個 C99 double complex 區塊。

STRSXP

lengthtruelength,後接一個指標區塊(指向 CHARSXPSEXP)。

DOTSXP

一種特殊的 LISTSXP 類型,用於綁定到 ... 符號的值:承諾的對列表。

ANYSXP

這用作任何類型的佔位符:沒有這種類型的實際物件。

VECSXP
EXPRSXP

lengthtruelength,後接一個指標區塊。這些在內部是相同的(並且與 STRSXP 相同),但對元素的解釋不同。

BCODESXP

針對編譯器產生的「位元組碼」物件。

EXTPTRSXP

有三個指標,指向指標、保護值(一個 R 物件,如果存在則保護此物件)和標籤(一個 SYMSXP?)。

WEAKREFSXP

一個 WEAKREFSXP 是一個長度為 4 的特殊 VECSXP,元素為「key」、「value」、「finalizer」和「next」。「key」為 NULL、環境或外部指標,而「finalizer」為函式或 NULL

RAWSXP

lengthtruelength,後接一區塊位元組。

S4SXP

兩個未使用的指標和一個標籤。


1.1.4 配置類別

如我們所見,標頭中的欄位 gccls 是三個位元,用於標示最多 8 個類別的節點。非向量節點屬於類別 0,而「小型」向量節點屬於類別 1 至 5,自訂配置器向量節點為類別 6,「大型」向量節點為類別 7。這些「小型」向量節點能夠儲存長度最多為 8、16、32、64 和 128 位元組的向量資料:較大的向量會個別使用 malloc 配置,而「小型」節點會從約 2000 位元組的頁面配置。使用自訂配置器配置的向量節點(透過 allocVector3)不會計入 gc 記憶體使用統計資料,因為其記憶體語意不受 R 控制,而且可能是非標準的(例如,記憶體可能在節點間部分共用)。


1.2 環境與變數查詢

使用者認為的「變數」是繫結到「環境」中物件的符號。R 中「環境」一詞含糊地用於表示 ENVSXP(符號值對的配對清單)的框架 ENVSXP,框架加上封裝。

有其他可以查詢「變數」的地方,在程式碼中的註解中稱為「使用者資料庫」。這些似乎沒有在 R 來源中記載,但顯然是指以前在 https://www.omegahat.net/RObjectTables/ 可用的 RObjectTable 套件。

基本環境很特別。有一個封裝為空環境 R_EmptyEnvENVSXP 環境,但該環境的框架並未被使用。反之,它的繫結是全域符號表的部份,是全域符號表中值不是 R_UnboundValue 的符號。當 R 啟動時,內部函式會(由 C 程式碼)安裝到符號表中,原始函式具有值,而 .Internal 函式具有 INTERNAL 巨集存取的欄位中的值。然後計算 .Platform.Machine,並將基本套件載入基本環境,後接系統設定檔。

環境(和符號表)的框架通常會進行雜湊,以加快存取速度(包括插入和刪除)。

預設情況下,R 會維護一個「變數」(即符號及其繫結)的(雜湊)全域快取,其中包含已找到的變數,且這僅指已標記為參與的環境,包括全域環境(又稱為使用者工作區)、基礎環境以及已attach的環境4。當環境attachdetach時,其符號的名稱會從快取中清除。在從全域環境搜尋變數時(可能是遞迴搜尋的一部分),會使用快取。


1.2.1 搜尋路徑

S 有「搜尋路徑」的概念:「變數」的查詢會(可能透過一系列的框架)導向「工作階段框架」、「工作目錄」,然後沿著搜尋路徑進行。搜尋路徑是一系列的資料庫(由search()傳回),其中包含系統函式(但不一定在路徑的尾端,因為預設情況下,會在尾端新增等同於套件的項目)。

R 對 S 模型進行了變更。有一個搜尋路徑(也由search()傳回),其中包含全域環境(又稱為使用者工作區),接著是已附加的環境,最後是基礎環境。請注意,與 S 不同,無法在工作區之前或基礎環境之後附加環境。

然而,在 R 中,變數查詢的概念更為廣泛,因此本小節標題中使用複數。由於環境具有封裝,因此從任何環境中都可以找到一個搜尋路徑,方法是查看框架,然後查看其封裝的框架,依此類推。由於不允許迴圈,因此這個程序最終會終止:它可以在基本環境或空環境中終止。(從概念上來說,可以將搜尋視為總是終止於空環境,但經過最佳化後會在基本環境中停止。)因此,「搜尋路徑」描述了在搜尋到達全域環境後所遍歷的環境鏈。


1.2.2 名稱空間

名稱空間是與套件關聯的環境(基本套件再次是特殊的,將另外討論)。套件 pkg 定義兩個環境 namespace:pkgpackage:pkg:可以 attach package:pkg 並使其成為搜尋路徑的一部分。

套件中 R 程式碼定義的物件是具有 namespace:pkg 環境中繫結的符號。 package:pkg 環境是由 namespace:pkg 環境中選取的符號(匯出)所組成。此環境的封裝是一個由其他名稱空間中的明確匯入所組成的環境,而環境的封裝是基本名稱空間。(因此,匯入在名稱空間環境中的假象是透過環境樹建立的。)基本名稱空間的封裝是全域環境,因此從套件名稱空間的搜尋會透過(明確和隱含的)匯入到標準「搜尋路徑」。

基本命名空間環境 R_BaseNamespace 是另一個特殊情況的 ENVSXP。它實際上與基本環境 R_BaseEnv 相同,除了它的封裝是全域環境,而不是空環境:內部程式碼將其框架中的查詢轉移到全域符號表。


1.2.3 哈希表

R 中的環境通常有哈希表,而現在這是 new.env() 中的預設值。它儲存在 VECSXP 中,其中 length 用於表的已配置大小,而 truelength 是正在使用的主要槽數目—指向 VECSXP 的指標是 ENVSXP 類型的 SEXP 標頭的一部分,如果環境未進行雜湊處理,則此指標指向 R_NilValue

有關雜湊的優缺點,請參閱電腦科學的基本教科書。

實作雜湊環境的程式碼在 src/main/envir.c 中。除非另行設定(例如透過 new.env()size 引數),否則初始表大小為 29。一旦負載因子(正在使用的主要槽的比例)達到 85%,表將按 1.2 的因子調整大小。

雜湊鏈儲存在 VECSXP 的 pairlist 元素中:項目插入到 pairlist 的前面。雜湊主要是為了快速搜尋環境,環境會不時新增,但很少刪除,因此項目並未實際刪除,而是將其值設定為 R_UnboundValue


1.3 屬性

正如我們所見,每個 SEXPREC 都有一個指向節點屬性的指標(預設為 R_NilValue)。屬性可以使用巨集/函式 ATTRIBSET_ATTRIB 存取/設定,但此類直接存取通常僅用於檢查屬性是否為 NULL 或重設屬性。否則,存取會透過函式 getAttribsetAttrib 進行,這些函式對屬性施加限制。需要注意的一點是,如果您將屬性從一個物件複製到另一個物件,您可能會(取消)設定 "class" 屬性,因此也需要複製物件和 S4 位元。有一個巨集/函式 DUPLICATE_ATTRIB 可以自動執行此操作。

請注意,CHARSXP 的「屬性」用於管理 CHARSXP 快取的一部分:當然,CHARSXP 對使用者而言並不可見,但 C 層級程式碼可能會查看其屬性。

此程式碼假設節點的屬性為 R_NilValue 或長度非零的配對清單(且由 SET_ATTRIB 檢查)。屬性以標籤(配對清單上的標籤)命名。替換函式 attributes<- 確保 "dim" 在配對清單中出現在 "dimnames" 之前。屬性 "dim" 是經過特別處理的屬性之一:會檢查其值,並移除任何 "names""dimnames" 屬性。同樣地,您無法在未設定 "dim" 的情況下設定 "dimnames",且所指派的數值必須是長度正確的清單,且其元素長度也必須正確(所有長度為零的元素都將替換為 NULL)。

給予特殊處理的其他屬性包括 "names""class""tsp""comment""row.names"。對於類似配對清單的物件,名稱不會儲存為屬性,而是 (作為符號) 標籤:不過 R 介面讓它們看起來像是傳統屬性,而對於一維陣列,它們會儲存為 "dimnames" 屬性的第一個元素。C 程式碼確保 "tsp" 屬性為 REALSXP,頻率為正,且隱含長度與指派物件的行數相符。類別和註解僅限於字元向量,且指派零長度註解或類別會移除屬性。設定或移除 "class" 屬性會適當地設定物件位元。整數列名稱會轉換為內部緊湊表示法,反之亦然。

在為具有非標準複製語意的類型物件新增屬性時,需要小心。僅有一個 NILSXP 類型的物件,R_NilValue,且不應具有屬性 (且在 installAttrib 中強制執行)。對於環境、外部指標和弱參考,屬性應與物件的所有用途相關:例如,為環境命名是合理的,也為套件中 R 程式碼填入的那些環境設定 "path" 屬性。

何時在物件上執行作業時,屬性應予保留?Becker、Chambers 和 Wilks (1988,第 144–6 頁) 提供了一些指導方針。純量函數(逐一元件對向量執行的函數,其輸出與輸入類似)應保留屬性(可能除了類別,如果保留類別,則需要保留 OBJECT 和 S4 位元)。二元運算通常會呼叫 copyMostAttrib 從較長的參數複製大部分屬性(如果長度相同,則優先使用第一個參數的值)。在此,「大部分」表示除了 namesdimdimnames 之外,所有屬性都由運算子的程式碼適當地設定。

子集化(除了使用空索引)通常會捨棄所有屬性,除了 namesdimdimnames,這些屬性會適當地重設。另一方面,子指派通常會保留此類屬性,即使長度已變更。強制轉換會捨棄所有屬性。例如

> x <- structure(1:8, names=letters[1:8], comm="a comment")
> x[]
a b c d e f g h
1 2 3 4 5 6 7 8
attr(,"comm")
[1] "a comment"
> x[1:3]
a b c
1 2 3
> x[3] <- 3
> x
a b c d e f g h
1 2 3 4 5 6 7 8
attr(,"comm")
[1] "a comment"
> x[9] <- 9
> x
a b c d e f g h
1 2 3 4 5 6 7 8 9
attr(,"comm")
[1] "a comment"

1.4 內容

內容是用於追蹤運算已進行到哪裡(以及從哪裡開始)的內部機制,讓控制流程結構可以運作,並可在錯誤情況下產生合理的資訊(例如 透過追蹤),以及其他情況(sys.xxx 函數)。

執行內容是 C structs 的堆疊

typedef struct RCNTXT {
    struct RCNTXT *nextcontext; /* The next context up the chain */
    int callflag;               /* The context ‘type’ */
    JMP_BUF cjmpbuf;            /* C stack and register information */
    int cstacktop;              /* Top of the pointer protection stack */
    int evaldepth;              /* Evaluation depth at inception */
    SEXP promargs;              /* Promises supplied to closure */
    SEXP callfun;               /* The closure called */
    SEXP sysparent;             /* Environment the closure was called from */
    SEXP call;                  /* The call that effected this context */
    SEXP cloenv;                /* The environment */
    SEXP conexit;               /* Interpreted on.exit code */
    void (*cend)(void *);       /* C on.exit thunk */
    void *cenddata;             /* Data for C on.exit thunk */
    char *vmax;                 /* Top of the R_alloc stack */
    int intsusp;                /* Interrupts are suspended */
    SEXP handlerstack;          /* Condition handler stack */
    SEXP restartstack;          /* Stack of available restarts */
    struct RPRSTACK *prstack;   /* Stack of pending promises */
} RCNTXT, *context;

加上位元組碼編譯器的其他欄位。這些「類型」來自

enum {
    CTXT_TOPLEVEL = 0,  /* toplevel context */
    CTXT_NEXT     = 1,  /* target for next */
    CTXT_BREAK    = 2,  /* target for break */
    CTXT_LOOP     = 3,  /* break or next target */
    CTXT_FUNCTION = 4,  /* function closure */
    CTXT_CCODE    = 8,  /* other functions that need error cleanup */
    CTXT_RETURN   = 12, /* return() from a closure */
    CTXT_BROWSER  = 16, /* return target on exit from browser */
    CTXT_GENERIC  = 20, /* rather, running an S3 method */
    CTXT_RESTART  = 32, /* a call to restart was made from a closure */
    CTXT_BUILTIN  = 64  /* builtin internal function */
};

其中,只要涉及函式封閉,CTXT_FUNCTION 位元就會開啟。

呼叫 begincontext 會建立內容,呼叫 endcontext 會結束內容:程式碼可以透過 findcontext 在堆疊中搜尋特定類型的內容(並跳轉到該處),或透過 R_JumpToContext 跳轉到特定內容。R_ToplevelContext 是「閒置」狀態(通常是命令提示字元),R_GlobalContext 是堆疊的最上層。

請注意,呼叫封閉會設定內容,但內部函式永遠不會設定,而原始內建函式只會在進行分析或作為外部函式的介面時設定。

位元組碼編譯器會在編譯時產生一個指令對應至原始參照和表達式的對應表,這讓我們可以在錯誤狀況下產生資訊。作為最佳化,位元組碼直譯器在某些情況下不會設定內容,例如在簡單迴圈中,或是在內嵌簡單內建函式或內部函式包裝器時。

從 S3 通用函式進行分派(透過 UseMethod 或其內部等效函式),或呼叫 NextMethod 會將內容類型設定為 CTXT_GENERIC。這用於將方法呼叫的 sysparent 設定為 genericsysparent,因此方法看起來像是取代通用函式呼叫,而不是從通用函式呼叫。

R sys.framesys.call 函數透過從內容堆疊的任一端計算對封閉函數 (類型 CTXT_FUNCTION) 的呼叫次數來運作。

請注意,結構的 sysparent 元素與 sys.parent() 不同。元素 sysparent 主要用於管理正在評估的函數的變更,亦即透過 Recall 和方法調用。

CTXT_CCODE 內容目前用於 cat()load()scan()write.table() (在發生錯誤時關閉連線),透過 PROTECT、序列化 (從錯誤中復原,例如釋放緩衝區) 以及在錯誤處理程式碼中 (提高 C 堆疊限制並重設一些變數)。


1.5 參數評估

正如我們所見,R 中的函數有 3 種類型,封閉函數 (SEXPTYPE CLOSXP)、特殊函數 (SPECIALSXP) 和內建函數 (BUILTINSXP)。在本節中,我們考慮函數呼叫的實際參數何時 (以及是否) 進行評估。內部 (特殊/內建) 函數和 R 層級函數 (封閉函數) 的規則不同。

對於封閉的呼叫,實際和形式的引數會進行比對,並建構一個比對的呼叫(另一個 LANGSXP)。此程序首先將實際的引數清單替換為對所提供值的承諾清單。接著建構一個新的環境,其中包含比對到實際或預設值的正式參數名稱:所有比對的值都是承諾,預設值是承諾在剛建立的環境中進行評估。然後該環境用於評估函式的本體,並且在遇到承諾時會強制執行(因此評估實際或預設的引數)。(評估承諾會在其值上設定 NAMED = NAMEDMAX,因此如果引數是符號,則在評估封閉呼叫期間,其繫結會被視為具有多個參考。)[NAMED 機制已由參考計數取代。]

如果封閉是一個 S3 通用函式(也就是包含對 UseMethod 的呼叫),則評估程序會相同,直到遇到 UseMethod 呼叫。在那個時間點,用於進行分派的引數(通常是第一個)會在尚未評估的情況下進行評估。如果已找到一個是封閉的方法,則會為其建立一個新的評估環境,其中包含方法的比對引數,以及在評估通用函式本體期間迄今定義的任何新變數。(請注意,這表示在呼叫方法時,會捨棄對通用函式本體中正式引數值的變更,但已強制的實際引數承諾會保留在強制執行時找到的值。另一方面,遺失的引數具有承諾使用由方法而非通用函式提供的預設值的承諾。)如果找到的方法是基本方法,則會使用通用函式使用的比對的承諾引數清單(可能已強制執行)來呼叫該方法。

特殊函數和內建函數的本質區別5在於,特殊函數的參數在呼叫 C 程式碼之前不會被評估,而內建函數的參數會。請注意,特殊函數/內建函數與原始函數或 .Internal 是分開的:quote 是特殊原始函數,+ 是內建原始函數,cbind 是特殊 .Internal,而 grep 是內建 .Internal

許多內部函數是內部泛函數,對特殊函數來說,表示它們在呼叫時不會評估其參數,但 C 程式碼會從呼叫 DispatchOrEval 開始。後者會評估第一個參數,並根據其類別尋找方法。(如果 S4 分派開啟,即使對於 S3 類別,也會先尋找 S4 方法。)如果找到方法,它會根據承諾評估剩餘參數的呼叫,將其分派到該方法。如果找不到方法,則在返回到內部泛函數之前評估剩餘參數。

內部函數可以成為泛函數的另一種方式是成為群組泛函數。大多數此類函數都是內建函數(因此會立即評估其所有參數),並且都包含呼叫 C 函數 DispatchGeneric。對於 "Math" 群組泛函數的參數數量有一些特殊情況,有些成員只允許一個參數,有些有兩個(第二個參數有預設值),而 trunc 允許一個或多個,但預設方法只接受一個。


1.5.1 遺漏

傳遞給(非內部)R 函式的實際引數,其數量可能少於函式的形式引數所需數量。如果從未使用引數(透過延遲求值),則未配對的形式引數並無關係,但當評估引數時,會評估其預設值(在函式的評估環境中),或擲出錯誤訊息,內容類似於

argument "foobar" is missing, with no default

內部遺漏由兩種機制處理。物件 R_MissingArg 用於指出形式引數沒有(預設)值。在將實際引數與形式引數配對時,會從形式引數建立一個新的引數清單,其所有值均為 R_MissingArg,並設定第一個 MISSING 位元。然後,每當形式引數與實際引數配對時,新引數清單的對應成員會將其值設定為配對的實際引數值,如果該值不是 R_MissingArg,則會取消設定遺漏位元。

此新引數清單用於為函式建立評估架構,如果稍後給予命名引數一個新值(在評估之前),則會清除遺漏位元。

參數的遺漏值可透過 missing() 函數查詢。如果參數的遺漏位元已設定,或其值為 R_MissingArg,則該參數明顯遺漏。然而,遺漏值可能會從一個函數傳遞到另一個函數,因為在函數呼叫中使用形式參數作為實際參數並不視為評估。因此 missing() 必須檢查尚未評估的形式參數的值(一個承諾),以查看它是否可能遺漏,這可能涉及調查承諾等……

特殊的原語也需要處理遺漏的參數,在某些情況下(例如 log),這就是它們特殊且非內建的原因。這通常是透過測試參數的值是否為 R_MissingArg 來完成的。


1.5.2 省略號參數

在撰寫函數時,省略號參數很方便,但會使參數評估的內部程式碼複雜化。

具有 ... 參數的函數的形式參數將其表示為單一參數,就像任何其他參數一樣,標記為符號 R_DotsSymbol。當實際參數與形式參數相符時,... 參數的值為 SEXPTYPE DOTSXP,這是承諾的成對清單(用於匹配的參數),但由 SEXPTYPE 區分。

回想一下,函數的評估框架最初包含匹配呼叫中的 name=value 對,因此 ... 也是如此。 ... 的值是一個(特殊)對列表,其元素由特殊符號 ..1..2、… 引用,這些符號設定了 DDVAL 位元:當遇到其中一個時,會在評估框架中 ... 符號的值中查詢(透過 ddfindVar)。

... 參數匹配的參數值可能會遺失。

特殊基本類型可能需要處理 ... 參數:例如,請參閱檔案 src/main/builtin.cswitch 的內部程式碼。


1.6 自動列印

頂層 R 表達式的傳回值是否列印是由全域布林變數 R_Visible 控制。根據檔案 src/main/names.c 中表格的 eval 欄,在進入所有基本和內部函數時設定(為 true 或 false):適當的設定可以透過巨集 PRIMPRINT 萃取。

R 基本函數 invisible 使用此機制:它只在進入前設定 R_Visible = FALSE 並傳回其參數。

對於大多數函數而言,輸入時 R_Visible 的設定值會在回傳時使用,但仍有例外。R 函數 identifyoptionssystemwriteBin 會決定結果是否應從引數或使用者動作中顯示。其他函數本身會呼叫可能會變更可見旗標的函數:範例6.Internaldo.callevalwithVisibleifNextMethodRecallrecordGraphicsstandardGenericswitchUseMethod

「特殊」基元和內部函數會在設定 R_Visible 之後 內部評估其引數,而引數的評估(例如 PR#9263 中的指定)可能會變更旗標的值。

在函數評估期間,R_Visible 旗標也可能會變更,程式碼中關於 warningwriteChar 和呼叫 GText 的圖形函數的註解(PR#7397)。(由於 C 層級函數 eval 會設定 R_Visible,因此這可能會套用至任何呼叫它的函數。由於在評估承諾時會呼叫它,因此連物件查詢都可能會變更 R_Visible。)除非允許 C 程式碼變更(上述例外情況由值為 2 的 PRIMPRINT 指示),否則內部和基元函數會強制執行回傳時 R_Visible 的文件設定。

實際的自動列印是由 PrintValueEnv 在檔案 print.c 中完成的。如果要列印的物件設定了 S4 位元,且 S4 方法調用已開啟,就會呼叫 show 來列印物件。否則,如果設定了物件位元(因此物件具有 "class" 屬性),就會呼叫 print 來調用方法:對於沒有類別的物件,會呼叫 print.default 的內部程式碼。


1.7 寫入障礙和垃圾收集器

R 長期以來一直有世代垃圾收集器,而 sxpinfo 標頭中的位元 gcgen 用於此實作。這與 mark 位元結合使用,以識別兩個前一代。

有三個層級的收集。0 層級只收集最年輕的一代,1 層級收集兩個最年輕的一代,2 層級收集所有世代。在 20 次 0 層級收集後,下一次收集會在 1 層級,在 5 次 1 層級收集後會在 2 層級。此外,如果 n 層級收集無法提供 20% 的可用空間(對於每個節點和向量堆),下一次收集將會在 n+1 層級。(R 層級函式 gc() 執行 2 層級收集。)

世代收集器需要有效地「老化」物件,特別是類似清單的物件(包括 STRSXP)。這可透過確保清單的元素被視為至少與清單一樣舊在它們被指派時來完成。這由函式 SET_VECTOR_ELTSET_STRING_ELT 處理,這就是它們是函式而不是巨集的原因。確保此類操作的完整性稱為寫入障礙,並透過使 SEXP 不透明且僅透過函式(無法在 C 中的指派中用作左值)提供存取來完成。

R 延伸模組中的所有程式碼都在寫入障礙之後。R 延伸模組無法直接存取 SEXPREC 的內部結構。如果定義了「USE_RINTERNALS」,則基本程式碼可以存取內部結構。這通常在編譯 R 時定義在 Defn.h 中。若要啟用對存取方式的檢查,可以使用旗標 --enable-strict-barrier 編譯 R,這可確保標頭 Defn.h 沒有定義「USE_RINTERNALS」,因此 SEXP 在 R 本身的大部分中都是不透明的。(有一些必要的例外:最重要的是在定義存取器函式的檔案 memory.c 中,以及需要存取內部結構大小的檔案 size.c 中。)

有關背景文件,請參閱 https://homepage.stat.uiowa.edu/~luke/R/barrier.htmlhttps://homepage.stat.uiowa.edu/~luke/R/gengcnotes.html


1.8 序列化格式

R 物件的序列化版本由 load/save 使用,並且 saveRDS/readRDS(及其較早的「內部」點名稱版本)和 serialize/unserialize 也在較低層級使用。這些版本在序列化目標(檔案、連線、原始向量)和是否要序列化單一物件或物件集合(通常是工作空間)方面有所不同。save 會在檔案開頭寫入標頭(單一 LF 終止行),而較低層級的版本則不會。

savesaveRDS 允許各種形式的壓縮,而 gzip 壓縮是預設值(ASCII 儲存除外)。壓縮套用於整個檔案串流,包括標頭,因此序列化檔案可以由外部程式解壓縮或重新壓縮。loadreadRDS 都可以在從檔案讀取時讀取 gzipbzip2xz 形式的壓縮,以及在從連線讀取時讀取 gzip 壓縮。

R 從 2001 年 12 月的 R 1.4.0 到 2019 年 3 月的 R 3.5.3 使用相同的序列化格式,稱為「版本 2」。自推出以來,它已以向後相容的方式進行擴充,例如支援額外的 SEXPTYPE。較早的格式仍透過 loadsave 提供支援,但此類格式在此處未說明。目前的預設序列化格式稱為「版本 3」,已在 R 3.5.0 中引入。

save 的運作方式是寫入單行標頭(通常是 RDX2\n,用於二進位儲存:目前唯一的其他值是 RDA2\n,用於 save(files=TRUE)),然後建立要儲存物件的標記配對清單,並序列化該單一物件。 load 會讀取標頭行,取消序列化單一物件(配對清單或向量清單),並將物件的元素指定在指定的環境中。標頭行在 R 中有兩個用途:它識別序列化格式,以便 load 可以切換到適當的讀取器程式碼,而換行符號 \n 則可以偵測已進行非二進位傳輸且重新對應行尾的檔案。它也可以被視為 file 程式所使用的「魔術數字」(儘管 R 儲存檔案預設上尚未被該程式所識別)。

R 中的序列化需要考慮物件可能包含對環境的參照,而這些環境又具有封閉環境,依此類推。(被識別為套件或名稱空間環境的環境會以名稱儲存。)有一些「參照物件」在複製時不會重複,且應該在取消序列化時保持共用。這些是弱參照、外部指標以及與套件、名稱空間和全域環境無關的環境。這些會透過雜湊表處理,而第一個之後的參照會寫成由表條目索引的參照標記。

版本 2 序列化首先寫入一個標頭,表示格式(通常為 XDR 格式二進位儲存的「X\n」,但「A\n」(ASCII)和「B\n」(原生字元順序二進位)也可能出現),然後寫入三個整數,提供格式版本和兩個 R 版本(由 Rversion.h 中的 R_Version 巨集封裝)。(非序列化將兩個版本解譯為寫入檔案的 R 版本,後接讀取格式所需的 R 最小版本。)序列化接著使用 src/main/serialize.c 檔案中的函式 WriteItem,遞迴寫出物件。

有些物件寫入時就像 SEXPTYPE:這些偽 SEXPTYPE 包括 R_NilValueR_EmptyEnvR_BaseEnvR_GlobalEnvR_UnboundValueR_MissingArgR_BaseNamespace

對於所有 SEXPTYPENILSXPSYMSXPENVSXP 除外),序列化從一個整數開始,其中 SEXPTYPE 位於位元 0:77,後面接著物件位元、兩個位元表示是否有任何屬性,以及是否有標籤(對於 pairlist 類型),一個未使用的位元,然後是位元 12:27 中的 gp 欄位8。 類似 pairlist 的物件會寫入其屬性(如果有)、標籤(如果有)、CAR,然後是 CDR(使用尾端遞迴):其他物件會在自身之後寫入其屬性。原子向量物件會寫入其長度,後面接著資料:一般向量清單物件會寫入其長度,後面接著對每個元素呼叫 WriteItemCHARSXP 的程式碼會針對 NA_STRING 進行特殊處理,並將其寫入為長度 -1,沒有資料。長度不超過 2^31 - 1 會以這種方式寫入,而較長的長度(僅出現在 64 位元系統中)會寫入為 -1,後面接著上、下 32 位元作為整數(視為未簽署)。

環境以多種方式處理:正如我們所見,有些寫成特定的偽 SEXPTYPE。套件和名稱空間環境寫成偽 SEXPTYPE,後接名稱。「正常」環境寫成 ENVSXP,其中整數表示環境是否已鎖定,後接封裝、框架、「標籤」(雜湊表)和屬性。

在「XDR」格式中,整數和雙精度數寫成大端序:但格式並非完全 XDR(如 RFC 1832 所定義),因為位元組數量(例如 CHARSXPRAWSXP 類型的內容)寫成原樣,未填充至四個位元組的倍數。

「ASCII」格式寫入 7 位元字元。整數格式化為 %d(但 NA_integer_ 寫成 NA),雙精度數格式化為 %.16g(加上 NAInf-Inf),位元組格式化為 %02x。字串使用標準跳脫字元(例如 \t\013)寫入非列印和非 ASCII 位元組。

版本 3 序列化擴充版本 2,支援 ALTREP 框架物件的客製化序列化。它也會在序列化時儲存目前的原生編碼,這樣未標記的字串可以在不同原生編碼下執行的 R 中反序列化時轉換。


1.9 CHARSXPs 的編碼

R 中的字元資料儲存在 sexptype CHARSXP 中。

支援除了目前區域設定以外的編碼,特別是 UTF-8 和 Windows 上用於中日韓語言的多位元組編碼。表示 CHARSXP 編碼的有限方法是透過兩個用於宣告編碼為 Latin-1 或 UTF-8 的「一般用途」位元。 (請注意,字元向量有可能包含不同編碼的元素。) 列印和繪圖都會注意到宣告並將字串轉換為目前區域設定 (可能使用 <xx> 以十六進位位元組顯示目前區域設定中無效的位元組)。許多 (但不是全部) 字元處理函數將保留宣告或重新編碼字串。

參照作業系統的字串 (例如檔案名稱) 需要在某些作業系統 (例如 Windows) 上透過寬字元介面傳遞。

字元字串何時宣告為已知編碼?一種方法是透過 Encoding 直接執行。如果已知,剖析器會宣告編碼,透過 parseencoding 參數或 R 命令列中執行剖析的區域設定。 (其他方法會記錄在 Encoding 的說明頁面中。)

不需要宣告 ASCII 字串的編碼,因為它們可以在任何區域設定中運作。 ASCII 字串不應該有標記的編碼,因為輸入此類字串至 CHARSXP 快取時,任何編碼都會被忽略。

僅考慮 UTF-8 和 Latin-1 的基本原理在於,大多數系統都能產生 UTF-8 字串,而且這是我們最接近通用格式的字串。對於無法產生 UTF-8 字串的系統(例如缺少功能強大的 iconv 的系統),它們很可能使用 Latin-1,這是 R 的舊假設。然後,如果解析器遇到無法在當前字元集表示的 Unicode 點的「\uxxxx」跳脫字元,它可以傳回 UTF-8 編碼的字串。(這需要 MBCS 支援,而且過去僅在9 Windows 上啟用。)現在這已在所有平台上啟用,而且「\uxxxx」或「\Uxxxxxxxx」跳脫字元可確保已解析的字串會標記為 UTF-8。

現在大多數字元處理功能都會保留 UTF-8 編碼:有些注意事項在檔案 src/main/character.c 的最上方,以及檔案 src/library/base/man/Encoding.Rd 中。

圖形裝置提供處理 UTF-8 編碼字串的功能,而無需重新編碼為原生字元集,方法是將 hasTextUTF8 設定為「TRUE」,並提供預期 UTF-8 編碼輸入的函式 textUTF8strWidthUTF8。通常,符號字型會編碼在 Adobe Symbol 編碼中,但可以透過將 wantSymbolUTF8 設定為「TRUE」來重新編碼為 UTF-8。cairographics 的 Windows 埠有一個相當奇特的假設:它希望符號字型以 UTF-8 編碼,就好像它編碼在 Latin-1 中,而不是 Adobe Symbol:這是由 wantSymbolUTF8 = NA_LOGICAL 選擇的。

使用 MSVCRT 作為 C 執行時期的 Windows 沒有 UTF-8 區域設定,而是預期使用 UCS-210 字串。R(以標準 C 編寫)在沒有廣泛變更的情況下,在內部無法使用 UCS-2。 Rgui 主控台11 在內部使用 UCS-2,但會以原生編碼與 R 引擎進行通訊。為了讓 UTF-8 字串可以在 Rgui.exe 中以 UTF-8 列印,catprint 和自動列印會使用跳脫慣例(請參閱標頭檔 rgui_UTF8.h)。

「Unicode」(UCS-2LE)檔案在 Windows 世界中很常見,而且如果在傳遞給這些函式的未開啟連線中明確宣告編碼,readLinesscan 會在 Windows 上將它們讀取為 UTF-8 字串。

Windows 有多種關於目前區域設定編碼的概念,一種在 C 執行時期(C 函式庫)中,另一種是作用中編碼頁(系統區域設定)。當呼叫 Windows API 函式的非 UTF-16 變體(先前稱為 ANSI 呼叫)時,會使用作用中編碼頁,無論是直接呼叫,或透過 MinGW-w64 內部的 POSIX 封裝程式間接呼叫,從 R、R 套件和它們連結的函式庫中呼叫。雖然 R 已透過直接呼叫 Windows API 的 UTF-16 變體來處理許多案例,但它有時仍可能使用非 UTF-16 變體,而且主要為 POSIX 系統開發的外部函式庫通常也會這麼做。因此,為了讓 R 能在 Windows 上可靠地使用(非 ASCII)字串,Windows 上的 C 區域設定編碼和作用中編碼頁必須相同,而且它們預設相同。

Windows UCRT C 執行時期支援 UTF-8 地區設定。從前,作用中編碼頁為系統範圍設定,變更時需要重新開機,且不支援 UTF-8。後來已新增「測試版:使用 Unicode UTF-8 以支援全球語言」功能,將作用中編碼頁設定為 UTF-8,但這仍然需要重新開機,且會影響所有應用程式,其中許多應用程式無法正確使用該意外設定,因此無法實際使用。

自 Windows 10(版本 1903)、Windows Server 2022(LTSC)和 Windows Server 1903(半年頻道)起,Windows 允許在應用程式清單中將作用中編碼頁設定為 UTF-8。這只會變更特定應用程式的作用中編碼頁,且會同時將 UCRT C 地區設定變更為 UTF-8。Windows 的 R 4.2 使用此功能取得 UTF-8 作為 Windows 上的原生編碼。為了實現此目的,R 必須切換至 UCRT,而這反過來又需要建立 Rtools42。

較舊版本的 Windows 仍然依賴先前的編碼支援,其中原生編碼無法為 UTF-8。R 4.2 需要 UCRT 才能運作,但 UCRT 可以安裝在 Vista SP2 和 Windows Server 2008 SP2 以後的 Windows 上。它自 Windows 10 和 Windows Server 2016 起隨 Windows 附帶。


1.10 CHARSXP 快取

mkChar 建立的 CHARSXP 有一個全域快取,這個快取可確保具有相同內容的大部分 CHARSXP 共用儲存空間(「內容」包括任何已宣告的編碼)。並非所有 CHARSXP 都是快取的一部分,特別是「NA_STRING」並非快取的一部分。從 0.99.0 之前的 R save 格式重新載入的 CHARSXP 也不會快取(因為使用的程式碼已凍結,而且現存的範例極少)。

快取會記錄字串的編碼以及位元組:建立 CHARSXP 的所有要求都應透過呼叫 mkCharLenCE 進行。如果字串的位元組都是 ASCII 字元,則會忽略在 mkCharLenCE 呼叫中提供的任何編碼。


1.11 警告和錯誤

每個 warningstop 都具有兩個 C 層級等效項,warningwarningcallerrorerrorcall。這些配對之間的關係類似:warning 會試圖找出適當的呼叫,然後在成功時呼叫 warningcall,並將該呼叫作為第一個引數,如果失敗則呼叫 call = R_NilValue。當呼叫 warningcall 時,它會在列印中包含已分析的呼叫,除非 call = R_NilValue

warningerror 查看內容堆疊。如果最上層的內容不是 CTXT_BUILTIN 類型,它用於提供呼叫,否則下一個內容會提供呼叫。這表示當這些函數從基元或 .Internal 呼叫時,假設的呼叫不會是基元/.Internal,而是呼叫基元/.Internal 的函數。這正是 .Internal 所需要的,因為這會提供呼叫給封閉包裝器。(此外,對於 .Internal,呼叫是 .Internal 的引數,因此可能不會對應到任何 R 函數。)不過,這不太可能是基元所需要的。

結果是 warningcallerrorcall 通常應該用於從基元呼叫的程式碼,而 warningerror 應該用於從 .Internal 呼叫的程式碼(以及從 .Call.C 等呼叫的程式碼,呼叫不會向下傳遞)。不過,有兩個複雜情況。一是程式碼可能會從基元或 .Internal 呼叫,這種情況下 warningcall 可能比較合適。另一個涉及替換函數,呼叫曾經是下列形式

> length(x) <- y ~ x
Error in "length<-"(`*tmp*`, value = y ~ x) : invalid value

這對最終使用者來說難以接受。對於替換函數,堆疊最上方會有合適的內容,因此應該使用 warning。(.Internal 替換函數的結果,例如 substr<-,並非理想。)


1.12 S4 物件

[此部分目前是初步草稿,不應視為確定。說明假設未設定 R_NO_METHODS_TABLES。]


1.12.1 S4 物件的表示

S4 物件可以是任何 SEXPTYPE。它們可能是具有 S4 類別資訊的簡單類型物件(例如原子向量或函數),或是 S4SXP 類型。在所有情況下,「S4 位元」(「一般用途」欄位的位元 4)都會設定,並且可以透過巨集/函數 IS_S4_OBJECT 進行測試。

S4 物件是透過 new()12 建立,然後透過 C 函數 R_do_new_object 建立。這會複製類別的原型,新增類別屬性,並設定 S4 位元。所有 S4 類別屬性都應該是長度為 1 的字元向量,並具有提供類別定義的套件(或 .GlobalEnv)名稱(以字串形式)的屬性。由於 S4 物件具有類別屬性,因此會設定 OBJECT 位元。

目前尚不清楚如果從 S4 物件中移除類別屬性,或者是否應該允許移除時會發生什麼情況。


1.12.2 S4 類別

S4 類別儲存在建立它們的環境中,為 R 物件,名稱為 .__C__classname:因此,它們不會在預設情況下由 ls 列出。

物件是 S4 物件,類別為 "classRepresentation",由 methods 套件定義。

由於這些只是物件,因此受一般範圍規則約束,且可以像其他物件一樣匯入和匯出命名空間。指令 importClassesFromexportClasses 只是方便的方法,用於參照類別物件,而無需知道其內部「元名稱」(儘管 exportClasses 會透過 isClass 進行一些健全性檢查)。


1.12.3 S4 方法

方法的詳細資料儲存在環境中(通常隱藏在各自的命名空間中),非語法名稱格式為 .__T__泛型:套件,包含 MethodDefinition 類別的物件,這些物件適用於從特定套件衍生的命名泛型在目前環境中定義的所有方法(可能是 .GlobalEnv)。這有時稱為「方法表」。

例如,

 length(nM <- asNamespace("Matrix") )                    # 941 for Matrix 1.2-6
 length(meth <- grep("^[.]__T__", names(nM), value=TRUE))# 107 generics with methods
 length(meth.Ops <- nM$`.__T__Ops:base‘) # 71 methods for the ’Ops' (group)generic
 head(sort(names(meth.Ops))) ## "abIndex#abIndex" ... "ANY#ddiMatrix" "ANY#ldiMatrix" "ANY#Matrix"

在 R 執行階段,每個非原始泛型都有一個關聯的環境,其中包含物件 .AllMTable.Generic.Methods.MTable.SigArgs.SigLength.MTableAllMTable 是合併的方法表,分別包含直接定義和透過繼承定義的所有方法。 .Methods 是合併的方法清單。

從名稱空間匯出方法比匯出類別更為複雜。首先請注意,您並非匯出方法,而是指令 exportMethods 會匯出為特定泛型定義的所有方法:該程式碼也會將任何直接匯出的泛型新增至泛型清單。對於透過 exportMethods 列出或自行匯出的泛型,對應的環境會被匯出,因此會顯示在套件環境中(作為隱藏物件)。

內部為 S4 泛型的基本資料類型方法(請參閱下方)總是會被匯出,無論是否在 NAMESPACE 檔案中提及。

方法可透過指令 importMethodsFrom 或透過 import 匯入名稱空間來匯入。此外,如果泛型是透過 importFrom 匯入,其方法也會被匯入。在所有情況下,如果泛型在名稱空間中,它就會被匯入,因此 importMethodsFrom 最適合用於定義在其他套件中泛型上的方法。由於泛型的各種方法可以從多個不同的套件匯入,因此方法表會合併。

當套件附加時,methods:::cacheMetaData 會被呼叫以更新內部表格:只有可見的方法會被快取。


1.12.4 S4 分派的機制

此小節不討論如何選擇 S4 方法:請參閱 https://developer.r-project.org/howMethodsWork.pdf

對於所有非原始函數,對本身並非 S4 通用函數的現有函數設定方法會在目前環境中建立一個新物件,該物件是呼叫 standardGeneric,並將舊定義設為預設方法。此類 S4 通用函數也可以 透過 呼叫 setGeneric13 建立,並且是 R 語言中的標準封閉函數,其環境為建立它們的環境。隨著命名空間的出現,這會產生一些問題:如果 myfn 之前在具有名稱空間的套件中,則搜尋路徑上將會有兩個稱為 myfn 的函數,而會呼叫哪一個取決於使用哪個搜尋路徑。對於基本命名空間中的函數來說,情況最為明顯,因為會在任何其他套件中找到新建立的函數之前找到原始函數。

出於效率原因,原始函數的處理方式截然不同:這會導致不同的語意。setGeneric 不允許用於原始函數。methods 命名空間包含一個名為 .BasicFunsList 的清單,其名稱為原始函數:項目可能是 FALSE 或顯示有效定義的標準 S4 通用函數。當呼叫 setMethod(或 setReplaceMethod)時,它會失敗(如果清單項目為 FALSE)或在清單中給定的有效通用函數上設定方法。

幾乎所有基本函數的 S4 方法實際調用都依賴於 S3 調用機制,因此 S4 方法只能針對內部為 S3 通用函數的基本函數進行調用。當以 S4 物件作為第一個引數呼叫內部為 S3 通用函數的基本函數,且 S4 調用已開啟(亦即已載入 methods 名稱空間),DispatchOrEval 會呼叫 R_possible_dispatch(定義於檔案 src/main/objects.c 中)。(S3 群組通用函數的成員,其中包含所有通用運算子,處理方式略有不同:會檢查前兩個引數並呼叫 DispatchGroup。)R_possible_dispatch 首先會檢查內部表格,以查看是否已針對該通用函數設定任何 S4 方法(且目前已為該通用函數啟用 S4 調用),如果已設定,則會使用儲存在另一個內部表格中的方法進行 S4 調用。所有基本函數都在基礎名稱空間中,而此機制表示可以針對(某些)基本函數設定 S4 方法,且將永遠使用這些方法,這與在非基本函數上設定方法形成對比。

例外為 %*%,其為 S4 通用函數,但不是 S3 通用函數,因為其 C 程式碼包含直接呼叫 R_possible_dispatch

基本函數 as.double 很特別,因為 as.numericas.real 是其副本。methods 套件程式碼部分會以名稱指稱通用函數,部分會以函數指稱,並將 as.doubleas.real 對應到 as.numeric(因為這是匯出方法的套件所使用的名稱)。

語言的某些元素實作為基本函數,例如 }。這包括子集和子指派「函數」,它們是 S4 通用函數,再次依賴於 S3 調用。

安裝 methods 時會產生 .BasicFunsList,方法是計算所有基本函數,最初禁止所有函數使用的方法,然後為 .GenericArgsEnv 成員、S4 群組通用函數和檔案 BasicFunsList.R 中的簡短例外清單設定通用函數:目前包含子集和子指派運算子,以及 c 的覆寫。


1.13 記憶體配置器

R 的記憶體配置幾乎都透過檔案 src/main/memory.c 中的常式完成。

R 的其他部分應盡可能使用檔案 src/main/memory.c 提供的配置器,這些配置器也是 撰寫 R 擴充套件記憶體配置 推薦使用的方法 ,供 R 套件使用,也就是使用 R_allocR_CallocR_ReallocR_Free。由 R_alloc 配置的記憶體會在呼叫 vmaxset 重設「水位」後,由垃圾收集器釋放。原始函數和 .Internal 函數的包裝器程式碼(以及 .Call.External 的包裝器程式碼)會自動執行此動作,但 vmaxgetvmaxset 可用於在內部程式碼中重設水位,如果僅在短時間內需要記憶體的話。

到目前為止提到的所有記憶體配置方法都相當昂貴。所有 R 平台都支援 alloca,而且在幾乎所有情況下14,這都由編譯器管理,在 C 堆疊中配置記憶體,而且非常有效率。

使用 alloca 有兩個缺點。首先,它是脆弱的,需要小心避免寫入(甚至讀取)超出傳回的配置區塊的界限。其次,它會增加 C 堆疊溢出的風險。建議只將它用於較小的配置(最多數萬位元組),且

    R_CheckStack();

會在配置後立即呼叫(因為 R 的堆疊檢查機制會在距離堆疊限制足夠遠的地方發出警告,以允許適度使用 alloca)。(檔案 src/main/unique.c 中的 do_makeunique 提供了這兩點的範例。)

有一個替代檢查,

    R_CheckStack2(size_t extra);

會在嘗試配置 extra 位元組之前立即呼叫。

對於需要大小不一但通常很小的儲存區塊的各種函式,已使用替代策略,並已整合到標頭檔 src/main/RBufferUtils.h 中的常式。這會使用包含緩衝區、目前大小和預設大小的結構。呼叫

    R_AllocStringBuffer(size_t blen, R_StringBuffer *buf);

會將 buf->data 設定為至少 blen+1 位元組的記憶體區域。至少會使用預設大小,這表示對於小配置,可以重複使用相同的緩衝區。呼叫 R_FreeStringBufferL 會在配置超過預設值時釋放記憶體,而呼叫 R_FreeStringBuffer 會釋放所有配置的記憶體。

需要初始化 R_StringBuffer 結構,例如透過

static R_StringBuffer ex_buff = {NULL, 0, MAXELTSIZE};

使用預設大小 MAXELTSIZE = 8192 位元組。目前大多數用法都有靜態 R_StringBuffer 結構,這允許(預設大小的)緩衝區在呼叫中共享,例如 grep,甚至在函式之間:如果 R 允許並行評估執行緒,則需要變更此項。因此,慣用語法為

static R_StringBuffer ex_buff = {NULL, 0, MAXELTSIZE};
...
    char *buf;
    for(i = 0; i < n; i++) {
        compute len
        buf = R_AllocStringBuffer(len, &ex_buff);
        use buf
    }
    /*  free allocation if larger than the default, but leave
        default allocated for future use */
   R_FreeStringBufferL(&ex_buff);

1.13.1 R_alloc 的內部

R_alloc 使用的記憶體配置為 R 向量,類型為 RAWSXP。因此,配置單位為 8 位元組,並向上取整。目前,要求零位元組會傳回 NULL(但不應依賴此項)。基於歷史原因,在所有其他情況下,在向上取整之前會新增 1 位元組,因此配置永遠比要求的位元組多 1–8 位元組:這也不應依賴此項。

配置的向量會透過設定 R_VStack 受到保護,因為垃圾收集器會標記所有可以從該位置存取的內容。當向量 R_alloc 時,其 ATTRIB 指標會設定為目前的 R_VStack,且 R_VStack 會設定為最新的配置。因此,R_VStack 是透過 R_alloc 配置的向量之單一連結鏈。函式 vmaxset 會重設位置 R_VStack,且應設定為先前透過 via vmaxget 取得的值:取得值之後的配置將不再受到保護,因此可供垃圾收集。


1.14 全域和基本環境的內部使用

本節記載系統已知使用這些環境:目的是將此類使用降至最低或消除。


1.14.1 基本環境

圖形裝置系統在基本環境中維護兩個變數 .Device.Devices:兩個變數都始終設定。變數 .Devices 提供開啟裝置名稱的字元向量清單,而 .Device 是對應於目前活動裝置的元素。空裝置將始終開啟。

似乎有一個變數 .Options,一個提供目前選項設定的配對清單。但事實上這只是一個已指定值的符號,因此顯示為基本變數。

類似地,評估器會建立一個符號 .Last.value,顯示為基本環境中的變數。

錯誤可能會在基本環境中產生物件 .Tracebacklast.warning


1.14.2 全域環境

亂數產生器的種子儲存在全域環境中的物件 .Random.seed 中。

有些錯誤處理常式可能會在全域環境中產生物件:例如 dump.frames 預設會產生 last.dump

windows() 裝置會使用變數 .SavedPlots 來儲存已儲存圖形的顯示清單,以便稍後顯示。這被視為使用者建立的變數。


1.15 模組

R 使用儲存在 modules 目錄中的多個共用物件/DLL。這些是選擇「依需求」載入的程式碼部分,而不是連結為動態函式庫或併入主執行檔/動態函式庫。

對於其餘模組,其動機是它們會透過連結的函式庫載入大量的(通常是選用的)程式碼。

internet

內部 HTTP 和 FTP 客戶端以及套接字支援,連結到特定於系統的支援函式庫。這可能會載入 libcurl,在 Windows 上會載入 wininet.dllws2_32.dll

lapack

使用 LAPACK 函式庫的程式碼,並連結到 libRlapack 或外部 LAPACK 函式庫。

X11

(僅限類 Unix 系統。) X11()jpeg()png()tiff() 裝置。這些是選用的,並連結到 X11pangocairojpeglibpnglibtiff 函式庫中的一些或全部。


1.16 可視性


1.16.1 隱藏 C 進入點

我們利用在 控制可視性 中討論的可視性機制,在 撰寫 R 擴充套件 中,不需要在主要 R 可執行檔/動態函式庫(特別是在任何套件或模組中)之外的 C 進入點應加上 attribute_hidden 前綴。 最小化 R 動態函式庫中符號的可視性將加快連結到該函式庫(套件將執行此操作)的速度,並降低連結到同名的錯誤進入點的可能性。此外,在某些平台上,減少進入點的數量允許使用更有效率的 PIC 版本:超過一半的進入點都被隱藏了。隱藏變數(與函式不同)的一個方便方法是在標頭檔 Defn.h 中宣告它們為 extern0

所使用的可視性機制僅在某些編譯器和平台上可用,特別是在 Windows 上,那裡使用的是替代機制。如果條目列在檔案 src/gnuwin32/Rdll.hide 中,它們將不會在 R.dll 中提供。 該檔案中的條目以空格開頭,並且必須嚴格按照 C 區域設定中的字母順序排列(如果您更改它,請使用檔案中的 sort 來確保這一點)。透過這個檔案,可以隱藏 Fortran 和 C 輸入點:前者為小寫,並帶有底線作為字尾,而字尾名稱應包含在檔案中。有些輸入點僅存在於 Windows 上或僅需要在 Windows 上可見,而檔案 src/gnuwin32/Maintainters.notes 中提供了一些關於這些輸入點的說明。

由於減少可見輸入點數量的優點,因此應在可能的情況下將它們宣告為 attribute_hidden。請注意,這只會對共用 R 函式庫建置產生影響,因此需要小心不要隱藏套件合法使用的輸入點。因此,最好在建立新的輸入點時做出可視性決策,包括是否應將其包含在標頭檔案 Rinternals.h 中。可以在類似 Unix 的合理標準上建立共用 R 函式庫建置的可見輸入點清單,方法如下:

nm -g libR.so | grep ‘ [BCDT] ’ | cut -b20-

1.16.2 Windows DLL 中的變數

Windows 的獨特之處在於,它傳統上將變數的匯入處理方式與函式不同:從 DLL 匯入的變數在連結到(「匯入」)時需要指定前綴(通常為「_imp_」),但在從(「匯出」)連結時則不需要。詳細資訊取決於編譯器系統,並且在該埠的生存期間已針對 MinGW 進行了變更。它們主要隱藏在標頭檔案 R_ext/libextern.h 中定義的一些巨集後面。

在主 R 來源中(非函數)變數,需要在 R.dll 外部(在套件、模組或其他 DLL 中,例如 Rgraphapp.dll)中參照,應宣告為前置詞 LibExtern。主要使用在 Rinternals.h 中,但需要考慮任何公開標頭和 Defn.h

現在可以使用 ld 的 MinGW 埠的「自動匯入」功能,來修正從 DLL 的匯入(如果 R 是為 Cygwin 平台建立,就會發生這種情況)。然而,當 R 的 MinGW 建立在 1998 年左右時,這是不可能的,允許較少的可視性控制,而且不適用於其他 Windows 編譯器組件。

只有在 Windows 上編譯 R 來源時,才能檢查是否已正確處理。


1.17 延遲載入

延遲載入總是用於套件中的程式碼,但對於套件中的資料集來說是可選的(由套件維護者選擇)。當使用它的套件/命名空間載入時,套件/命名空間環境會使用所有命名物件的承諾來填滿:當這些承諾被評估時,它們會從資料庫載入實際程式碼。

有獨立的資料庫用於程式碼和資料,儲存在 Rdata 子目錄中。資料庫包含兩個檔案,name.rdbname.rdx.rdb 檔案是序列化物件的串接,而 .rdx 檔案包含索引。物件通常儲存在 gzip 壓縮格式中,其中 4 位元組標頭提供未壓縮的序列化長度(在 XDR 中,也就是大端序位元組順序),並透過呼叫基本 lazyLoadDBfetch 來讀取。(請注意,這使得延遲載入不適合於非常大的物件:R 物件的未序列化長度可能會超過 4GB。)

索引或「對應」檔案 name.rdx 是壓縮的序列化 R 物件,可由 readRDS 讀取。它是包含三個元素 variablesreferencescompressed 的清單。前兩個是長度為 2 的整數向量的命名清單,提供 name.rdb 檔案中序列化物件的偏移量和長度。元素 variables 有每個命名物件的條目:references 會將暫時環境序列化,用於將命名環境新增至資料庫時。compressed 是個邏輯值,表示序列化物件是否已壓縮:現今總是使用壓縮。我們稍後新增 compressed = 23 值,用於 bzip2xz 壓縮(未來可能擴充至其他方法):這些格式會在標頭中新增第五個位元組,表示壓縮類型,如果壓縮會擴充序列化物件,則會將其儲存為未壓縮。

由於效能考量,會特別處理來源參考:srcfile 環境中的繫結 linesparseData 會延遲載入。這會使用一種機制,允許延遲載入環境中的選取繫結。此類環境的關鍵字是一個包含兩個元素的清單:eagerKey 提供延遲載入繫結的長度為 2 的整數關鍵字,lazyKeys 提供一個長度為 2 的整數關鍵字向量,每個延遲載入繫結一個。

程式碼或資料的延遲載入資料庫載入器是 base 套件中的 lazyLoad 函式,但請注意,有一個個別的副本用於在 R_HOME/base/R/base 檔案中載入 base 本身。

延遲載入資料庫是由 src/library/tools/R/makeLazyLoad.R 中的程式碼建立的:主要工具是未匯出的函式 makeLazyLoadDB,而資料庫條目的插入是透過呼叫 .Call("R_lazyLoadDBinsertValue", ...) 進行的。

小於 10MB 的延遲載入資料庫在第一次使用時會快取在記憶體中:當使用具有高延遲的文件系統(可移除式裝置和 Windows 上的網路掛載文件系統)時,發現有此必要。

延遲載入資料庫載入到套件的匯出中,但不會載入到命名空間環境本身。因此,當套件附加時,它們會顯示出來,而且也可以透過:: 算子顯示出來。這是經過深思熟慮的設計決策,因為套件大多讓資料集可供最終使用者(或其他套件)使用,而且不應優先從套件中的函式中找到它們,讓預期使用一般搜尋路徑的使用者感到意外。(有一個替代機制 sysdata.rda,用於主要打算在套件內使用的「系統資料集」)。

使用相同的資料庫機制來儲存已剖析的 Rd 檔案。可以透過呼叫 tools:::fetchRdDB 來擷取一個或所有已剖析的物件。


2 .Internal.Primitive

在建置時間編譯到 R 的 C 程式碼可以在稱為基本函數中直接呼叫,或透過.Internal介面呼叫,其語法與.External介面非常類似。更精確地說,R 維護一個 R 函數名稱與對應 C 函數的表格,依慣例,所有函數都以「do_」開頭,並傳回SEXP。此表格(src/main/names.c檔案中的R_FunTab)也指定函數所需或允許的引數數量,引數在呼叫前是否需要評估,以及函數在意義上是否為「內部」,意即必須透過.Internal介面存取,或可直接存取,後者在 R 中會印成.Primitive

通常偏好將使用.Internal()包覆在封閉函數中的函數,因為這可確保對命名和預設引數的標準處理。例如,grep定義為

grep <-
function (pattern, x, ignore.case = FALSE, perl = FALSE, value = FALSE,
         fixed = FALSE, useBytes = FALSE, invert = FALSE)
{
    if (!is.character(x)) x <- structure(as.character(x), names = names(x))
    .Internal(grep(as.character(pattern), x, ignore.case, value,
                   perl, fixed, useBytes, invert))
}

,而使用as.character允許方法被調用(例如,對於因子)。

然而,基於方便性和效率(因為在封閉函數中使用.Internal介面會產生一些額外負擔),基本函數是可直接存取的例外。當然,基本函數對於基本運算而言是必要的,例如.Internal本身就是一個基本函數。請注意,基本函數不使用 R 程式碼,因此與一般解譯函數有很大的不同。特別是,formalsbody會為此類物件傳回NULL,而引數比對可以採用不同的處理方式。對於某些基本函數(包括callswitch.C.subset),位置比對非常重要,以避免對第一個引數進行部分比對。

原始函數的清單可能會變更;目前,它包括下列項目。

  1. 「特殊函數」,實際上是語言元素,但實作為原始函數
    {       (         if     for      while  repeat  break  next
    return  function  quote  switch
    
  2. 語言元素和基本運算子(即通常foo(a, b, ...)呼叫的函數),用於子集化、指派、算術、比較和邏輯
                   [    [[    $    @
    <-   <<-  =    [<-  [[<-  $<-  @<-
    
    +    -    *    /     ^    %%   %*%  %/%
    <    <=   ==   !=    >=   >
    |    ||   &    &&    !
    

    當算術、比較和邏輯運算子以函數呼叫時,任何引數名稱都會被捨棄,因此使用位置比對。

  3. 屬於下列函數群組之一的「低階」0 和 1 引數函數
    1. 具有單一引數的基本數學函數,即
      abs     sign    sqrt
      floor   ceiling
      
      exp     expm1
      log2    log10   log1p
      cos     sin     tan
      acos    asin    atan
      cosh    sinh    tanh
      acosh   asinh   atanh
      cospi   sinpi   tanpi
      
      gamma   lgamma  digamma trigamma
      
      cumsum  cumprod cummax  cummin
      
      Im  Re  Arg  Conj  Mod
      

      log是具有命名引數比對的一或兩個引數的原始函數。

      trunc是一個困難的案例:它是一個可以有一個或多個引數的原始函數:原始函數中處理的預設方法只有一個。

    2. 很少在「程式設計」之外使用的函數(即主要用於其他函數內部),例如
      nargs          missing        on.exit        interactive
      as.call        as.character   as.complex     as.double
      as.environment as.integer     as.logical     as.raw
      is.array       is.atomic      is.call        is.character
      is.complex     is.double      is.environment is.expression
      is.finite      is.function    is.infinite    is.integer
      is.language    is.list        is.logical     is.matrix
      is.na          is.name        is.nan         is.null
      is.numeric     is.object      is.pairlist    is.raw
      is.real        is.recursive   is.single      is.symbol
      baseenv        emptyenv       globalenv      pos.to.env
      unclass        invisible      seq_along      seq_len
      
    3. 程式設計和階段管理工具程式
      browser  proc.time  gc.time tracemem retracemem untracemem
      
  4. 下列基本替換和萃取函數
    length      length<-
    class       class<-
    oldClass    oldClass<-
    attr        attr<-
    attributes  attributes<-
    names       names<-
    dim         dim<-
    dimnames    dimnames<-
                environment<-
                levels<-
                storage.mode<-
    

    請注意,最佳化NAMED = 1僅在原始函數內有效(因為.Internal的封閉包裝器會在對引數的承諾評估時設定NAMED = NAMEDMAX),因此替換函數應盡可能為原始函數,以避免複製(至少在它們的預設方法中)。[NAMED機制已由參考計數取代。]

  5. 下列函數為原始函數,考量到效率因素
    :           ~           c           list
    call        expression  substitute
    UseMethod   standardGeneric
    .C          .Fortran   .Call        .External
    round       signif      rep         seq.int
    

    以及下列僅供內部使用的函數

    .Primitive      .Internal
    .Call.graphics  .External.graphics
    .subset         .subset2
    .primTrace      .primUntrace
    lazyLoadDBfetch
    

多個引數的原語

call       switch
.C         .Fortran   .Call       .External

有目的地使用位置匹配,並且需要這樣做,以避免對其第一個引數進行部分匹配。它們會檢查第一個引數是否未命名,或者對於前兩個引數,是否部分匹配正式引數名稱。另一方面,

attr       attr<-     browser     rememtrace substitute  UseMethod
log        round      signif      rep        seq.int

管理自己的引數匹配,並以標準方式工作。

所有單個引數的原語都會檢查,如果它們是用命名引數呼叫的,則此引數(部分)與文件中的名稱相符:這也會針對替換函數執行,其中一個引數加上

淨效應是,針對預期供最終使用者用作函數的原語進行引數匹配的方式,與針對已詮釋函數的方式相同,但有六個例外情況需要位置匹配。


2.1 特殊原語

少數的原語是 特殊 而不是 內建,也就是說,它們會以未評估的引數輸入。這對於語言建構和賦值運算子顯然是必要的,以及對於 &&||,它們有條件地評估它們的第二個引數,和 ~.Internalcallexpressionmissingon.exitquotesubstitute,它們不會評估它們的一些引數。

repseq.int 是特殊的,因為它們會評估它們的一些引數,有條件地取決於哪些是非遺失的。

logroundsignif 是特殊的,允許將預設值提供給遺失的引數。

子集化、子賦值和 @ 運算子都是特殊的。(對於萃取和替換形式,$@ 會採用符號引數,而 [[[ 允許遺失的引數。)

UseMethod 是特殊的,以避免額外的內容新增到對內建函式的呼叫中。


2.2 特殊內部函式

也有特殊的 .Internal 函式:NextMethodRecallwithVisiblecbindrbind(允許 deparse.level 引數)、eapplylapplyvapply


2.3 基本函數的原型

基本函數和運算子有原型,這些原型用於列印、args 和套件檢查(例如,tools::checkS3methods 和套件 codetools)。base 套件(和命名空間)中有兩個環境,「.GenericArgsEnv」是給那些是內部 S3 通用函數的基本函數,「.ArgsEnv」是給其他函數。這些環境包含與基本函數同名的封閉函數、從說明頁面(手動)衍生的形式參數、一個是對 UseMethodNULL 的合適呼叫的主體,以及 base 命名空間的環境。

print.defaultargs 的 C 程式碼優先使用這些環境中的封閉函數,而不是 base 中的定義(作為基本函數)。

QC 函數 undoc 檢查這些環境中建構原型的所有函數目前都是基本函數,而且未包含的基本函數最好視為語言元素(在撰寫時

$  $<-  &&  (  :  @  @<-  [  [[  [[<-  [<-  {  ||  ~  <-  <<-  =
break  for function  if  next  repeat  return  while

)。有人可能會爭論 ~,但它為剖析器所知,而且語意與一般函數非常不同。而 : 在兩個意義中有不同的參數名稱的文件。

QC 函數 codoccheckS3methods 也使用這些環境(有效地將它們置於搜尋路徑中的基礎之前),因此 codoc 會根據說明頁面檢查它們所包含函數的形式。但是,泛型基本原語存在兩個問題。第一個問題是,許多運算子都是 S3 群組泛型 Ops 的一部分,並且定義它們的引數為 e1e2:儘管這將非常不尋常,但運算子可以稱為例如 "+"(e1=a, e2=b),如果方法調用發生在封閉中,則會出現引數名稱不匹配。因此,環境 .GenericArgsEnv 中的定義必須使用引數名稱 e1e2,即使傳統文件是根據 xycodoc 通過 tools:::.make_S3_primitive_generic_env 進行適當的調整。第二個差異與 Math 群組泛型有關,其中群組泛型定義為引數清單 (x, ...),但大多數成員在用作預設方法時只允許一個引數(而 roundsignif 允許兩個作為預設方法):再次使用修復程式。

.GenericArgsEnv 中的基本原語會透過定義它們的方法來檢查(透過 tests/primitives.R)是否為泛型 via,並透過設定方法並檢查它未被調用,對其餘的基本原語進行檢查,以確定它們可能不是泛型(但這可能會因為其他原因而失敗)。但是,除了閱讀原始碼之外,沒有確定的方法可以知道其他 .Internal 或基本函數是否在內部是泛型的。


2.4 新增基本型別

[供 R 核心使用:反向執行此程序以移除基本型別。最常見的方式是將 .Internal 變更為基本型別或 反之亦然。]

基本型別列於 src/main/names.c 中的 R_FunTab 表格:基本型別在「eval」欄位中為「Y = 0」。

需要在 base 套件的說明檔案中新增「\alias」項目,且需要將基本型別新增至本節開頭的其中一個清單。

有些基本型別被視為語言元素(目前的基本型別已列於上方)。這些需要新增至 src/library/tools/R/QC.R 檔案中的 undoc() 中的 langElts 清單,以及 tests/primitives.R 中的 lang_elements 清單。

所有其他基本型別都被視為函式,且應列於 src/library/base/R/zzz.R 中定義的其中一個環境中,可能是 .ArgsEnv.GenericArgsEnv:內部泛型也需要列於字元向量 .S3PrimitiveGenerics 中。另請注意上方關於引數配對的討論:如果您透過轉換 .Internal 新增具有多個引數的基本型別函式,您需要將引數配對新增至 C 程式碼,而對於具有單一引數的基本型別函式,則需要新增引數名稱檢查。

請務必執行 make check-devel:這會測試大部分這些需求。


3 R 原始碼中的國際化

在 R 套件中標記訊息(錯誤、警告等)以進行翻譯的程序說明在 撰寫 R 擴充套件 中的 國際化 中,而隨 R 附帶的標準套件(grDeviceswindows() 裝置的選單除外)已以與其他套件相同的方式進行國際化。


3.1 R 程式碼

R 程式碼的國際化方式與擴充套件套件完全相同。由於所有具有 R 程式碼的標準套件也都有名稱空間,因此從不需要指定 domain,但為了效率,當訊息是 via gettextfgettextngettext 建構時,呼叫 messagewarningstop 應包含 domain = NA

對於每個套件,提取的訊息和翻譯來源儲存在原始套件的套件目錄 po 中,而編譯的翻譯則儲存在 inst/po 中,以便安裝到已安裝套件的套件目錄 po 中。這也適用於套件中的 C 程式碼。


3.2 主要 C 程式碼

主要 C 程式碼(例如 src/*/*.c 和模組中的檔案)是 R 最接近「gettext」編寫應用程式的類型。主要 C 程式碼中的訊息在網域 R 中,並儲存在頂層目錄 po 中,編譯的翻譯則儲存在 share/locale 中。

R 網域涵蓋的檔案清單指定在檔案 po/POTFILES.in 中。

標示訊息以進行翻譯的正常方式是透過 _("msg"),就像套件一樣。但是,有時需要標示段落進行翻譯,但不想在當時翻譯它們,例如在宣告字串常數時。這是 N_ 巨集的目的,例如

{ ERROR_ARGTYPE,           N_("invalid argument type")},

來自檔案 src/main/errors.c

巨集 P_

#ifdef ENABLE_NLS
#define P_(StringS, StringP, N) ngettext (StringS, StringP, N)
#else
#define P_(StringS, StringP, N) (N > 1 ? StringP: StringS)
#endif

可用作 ngettext 的包裝器:然而在某些情況下,較佳的方法是使用 ngettext 條件化(在 ENABLE_NLS)程式碼。

巨集 _("msg") 可安全地用於目錄 src/appl;獨立的「nmath」的標頭會略過可能的翻譯。(這不適用於 N_P_)。


3.3 Windows GUI 特定程式碼

Windows GUI 的訊息位於獨立的網域「RGui」中。這樣做的原因有兩個

  • Windows 版 R 的翻譯者可能與 R 其他部分的翻譯者不同(熟悉 GUI 有幫助),而且
  • Windows 的訊息最自然地以該語言的原生字元集處理,而對於 CJK 語言,字元集是 Windows 特定的。(事實證明,由於我們移植的 iconv 在 Windows 下運作良好,因此這一點比預期的不那麼重要。)

RGui」網域的訊息標示為 G_("msg"),此巨集定義在標頭檔 src/gnuwin32/win-nls.h 中。考慮的檔案清單以硬編碼方式定義在檔案 src/library/tools/R/translations.R 的函式 update_RGui_po 中,此函式透過檔案 po/Makefile.winupdate-RGui 目標呼叫:請注意,這包括 devWindows.c,因為 windows 裝置上的功能表被視為 GUI 的一部分。(還有 GN_("msg"),類似於 N_("msg")。)

RGui」網域的範本和訊息目錄位於套件 base 的頂層 po 目錄中。


3.4 macOS GUI

這部分另作處理:請參閱 https://developer.r-project.org/Translations30.html


3.5 更新

請參閱檔案 po/README,了解如何更新訊息範本和目錄。


4 已安裝套件結構

原始碼 套件的結構說明在 撰寫 R 擴充套件建立 R 套件:此章節探討已安裝 套件的結構。

已安裝套件有一個頂層檔案 DESCRIPTION,套件原始碼中同名檔案的副本,附加一個「Built」欄位,以及檔案 INDEX,通常描述可取得說明的物件,一個檔案 NAMESPACE(如果套件有命名空間),選用的檔案例如 CITATIONLICENCENEWS,以及從 inst 複製進來的任何其他檔案。它會有目錄 Metahelphtml(即使套件沒有說明頁面),幾乎總是有一個目錄 R,而且通常有一個目錄 libs 來包含已編譯的程式碼。R 已知其他有意義的目錄有 datademodocpo

函數 library 尋找命名空間,如果找到一個,則將控制權傳遞給 loadNamespace。然後 libraryloadNamespace 尋找檔案 R/pkgname,如果找不到,則發出警告,否則將程式碼(使用 sys.source)來源到套件的環境中,然後如果存在,則延遲載入資料庫 R/sysdata。因此,R 程式碼的載入方式取決於 R/pkgname 的內容:share/R/nspackloader.R 中提供了載入延遲載入資料庫的標準範本。

編譯的程式碼通常在套件的命名空間由 NAMESPACE 檔案中的 useDynlib 指令載入,或由套件的 .onLoad 函數載入。根據慣例,編譯的程式碼由呼叫 library.dynam 載入,這會在目錄 libs(如果使用子架構,則在適當的子目錄中)尋找共用物件(類 Unix)或 DLL(Windows)。

子目錄 data 有兩個用途。在使用資料延遲載入的套件中,它包含延遲載入資料庫 Rdata,以及檔案 Rdata.rds,其中包含命名字元向量,由 data() 在(不尋常的)事件中使用,用於此類套件。否則,它是來源中 data 目錄的副本,如果使用 R CMD INSTALL --resave-data,則重新壓縮已儲存的映像。

子目錄 demo 支援 demo 函數,並從來源複製。

子目錄 po 包含(在子目錄中)編譯的訊息目錄。


4.1 元資料

目錄 Meta 包含多個 .rds 格式的檔案,這些檔案是由 saveRDS 寫入的序列化 R 物件。所有套件都有檔案 Rd.rdshsearch.rdslinks.rdsfeatures.rdspackage.rds。有命名空間的套件有檔案 nsInfo.rds,有資料、示範或範例的套件有 data.rdsdemo.rdsvignette.rds 檔案。

這些檔案的結構(以及它們的存在和名稱)對 R 是私有的,因此這裡的說明是針對那些嘗試追蹤 R 來源的人:在非基本套件中不應參照這些檔案。

檔案 package.rds 是從 DESCRIPTION 檔案中萃取資訊的儲存檔。它是數個元件的清單。第一個「DESCRIPTION」是一個字元向量,DESCRIPTION 檔案由 read.dcf 讀取。其他元素「Depends」、「Suggests」、「Imports」、「Rdepends」和「Rdepends2」記錄「Depends」、「Suggests」和「Imports」欄位。這些都是清單,而且可以是空的。前三個欄位對每個已命名的套件都有條目,每個條目都是長度為 1 或 3 的清單,元素「name」(套件名稱)和選用元素「op」(字元字串)和「version」(類別「"package_version"」的物件)。元素「Rdepends」用於 R 的第一個版本相依性,而「Rdepends2」是零個或多個 R 版本相依性的清單,每個都是前面套件所述形式的三元素清單。元素「Rdepends」不再使用,但仍可能需要,因此 R < 2.7.0 可以偵測套件並未為其安裝。

檔案 nsInfo.rds 記錄一個清單,也就是 NAMESPACE 檔案的剖析版本。

檔案 Rd.rds 記錄一個資料框,每一列代表一個說明檔案。欄位有「檔案」(含副檔名的檔案名稱)、「名稱」(「\name」區段)、「類型」(來自選用的「\docType」區段)、「標題」、「編碼」、「別名」、「概念」和「關鍵字」。除了「別名」是字元向量的清單外,其他欄位都是字元向量。

檔案 hsearch.rds 記錄「help.search」要使用的資訊。這是一個清單,有四個未命名元素,它們是說明檔案、別名、關鍵字和概念的字元矩陣。所有矩陣都有「ID」和「套件」欄位,用來將別名、關鍵字和概念(最後三個元素的剩餘欄位)連結到特定的說明檔案。第一個元素有更多欄位,包括「LibPath」(儲存為 "",在檔案載入時填入)、「name」、「title」、「topic」(第一個別名,用於將結果呈現為「pkgname::topic」)和「編碼」。

檔案 links.rds 記錄一個命名字元向量,名稱是別名,值是下列形式的字元字串

"../../pkgname/html/filename.html"

檔案 data.rds 記錄一個兩欄字元矩陣,欄位包含資料集名稱和對應說明檔案的標題。檔案 demo.rds 對套件示範有相同的結構。

檔案 vignette.rds 記錄一個資料框,每一列代表一個「範例」(.[RS]nw 檔案在 inst/doc),欄位有「檔案」(來源中的完整檔案路徑)、「標題」、「PDF」(已安裝 PDF 版本的無路徑檔案名稱,如果存在)、「依賴」、「關鍵字」和「R」(已安裝 R 程式碼的無路徑檔案名稱,如果存在)。


4.2 說明

所有已安裝的套件,無論是否具有任何 .Rd 檔案,都具有 helphtml 目錄。後者通常只包含單一檔案 00Index.html,套件索引具有指向說明主題(如果有)的超連結。

目錄 help 包含檔案 AnIndexpaths.rdspkgname.rd[bx]。後兩個檔案是已剖析 .Rd 檔案的延遲載入資料庫,由 tools:::fetchRdDB 存取。檔案 paths.rds.Rd 檔案原始路徑名稱的已儲存字元向量,用於更新資料庫時。

檔案 AnIndex 是兩個欄的 tab 分隔檔案:第一個欄包含說明檔案中定義的別名,第二個欄包含包含該別名的檔案的基底名稱(不含 .Rd.rd 副檔名)。它由 utils:::index.search 讀取,用於搜尋符合主題(別名)的檔案,並由 scanutils:::matchAvailableTopics 中讀取,這是完成系統的一部分。

檔案 aliases.rds 是與 AnIndex 相同的資訊,作為已命名字元向量(名稱為主題,值為檔案基底名稱),以加快存取速度。


5 檔案

R 提供許多函式來處理檔案和目錄:其中許多函式最近才新增,以利於在 R 中撰寫指令碼,特別是將 R 指令碼取代 Perl 指令碼來管理 R 本身。

這些函式是由標準 C/POSIX 函式庫呼叫實作,Windows 除外。這表示檔案名稱必須編碼在目前的區域設定中,因為作業系統沒有提供其他方法來存取檔案系統:檔案名稱越來越常儲存在 UTF-8 中,而作業系統會在其他區域設定中將檔案名稱轉換為 UTF-8。因此,使用 UTF-8 區域設定可以透明地存取整個檔案系統。

Windows 是另一個故事。在 Windows 中,檔案名稱的內部檢視採用 UTF-16LE(所謂的「Unicode」),而標準 C 函式庫呼叫只能存取其名稱可以用目前編碼頁表示的檔案。為了解決此限制,有一組平行 Windows 特定的呼叫,這些呼叫會採用檔案路徑的寬字元引數。R 中許多檔案處理已改用這些函式,因此檔案名稱可以用 UTF-8 編碼字串在 R 中處理,轉換成寬字元(在 Windows 中為 UTF-16LE),並傳遞給作業系統。工具程式 RC_fopenfilenameToWchar 可協助此處理程序。目前 file.copy 到目錄、list.fileslist.dirspath.expand 僅能使用目前編碼頁編碼的檔案路徑。

所有這些函式都會執行波浪線展開,方式與 path.expand 相同,但 Sys.glob 故意例外。

檔案名稱可能區分大小寫或不區分大小寫:後者是 Windows 和 macOS 的常態,前者則是在其他類 Unix 上的常態。請注意,這是作業系統和檔案系統的特性:在掛載檔案系統時,通常可以將名稱對應到大小寫。這可能會影響 list.filesSys.glob 中模式的比對。

檔案名稱通常在 Windows 和 macOS 中包含空格,但在其他地方則否。由於檔案名稱由 R 處理為字串,因此空格通常不會造成問題,除非檔案名稱傳遞給其他程序,例如透過 system 呼叫。

Windows 還有其他一些特性。POSIX 檔案系統有一個單一根目錄(其他實體檔案系統會掛載到該根目錄下的邏輯目錄),而 Windows 則為每個實體或邏輯檔案系統(「磁碟區」)設定個別根目錄,並組織在 磁碟機(檔案路徑從 D: 開始,不分大小寫)和 網路共享(路徑類似 \netname\topdir\myfiles\a file)底下。有一個目前的磁碟機,沒有磁碟機部分的路徑名稱會相對於目前的磁碟機。此外,每個磁碟機都有目前的目錄,而相對路徑會相對於該目前的目錄,如果指定了特定磁碟機,則會相對於該磁碟機上的目錄。因此,D:dir\fileD: 是有效的路徑規格(後者是磁碟機 D: 上的目前目錄)。


6 圖形

R 的圖形內部結構經過重新設計,讓多個圖形系統能夠安裝在圖形「引擎」之上,目前有兩個這樣的系統:一個支援「基本」圖形(基於 S 中的圖形,其 R 程式碼15 在套件 graphics 中),另一個則實作在套件 grid 中。

可以在 https://www.stat.auckland.ac.nz/~paul/R/basegraph.htmlhttps://www.stat.auckland.ac.nz/~paul/R/graphicsChanges.html 找到一些關於歷史變更的說明。

在最底層的是圖形裝置,它管理繪圖表面(螢幕視窗或要寫入檔案的表示)。這會實作一組圖形原語,用於「繪製」

以及請求資訊,例如

以及請求/機會採取行動,例如

裝置也設定許多變數,主要是布林旗標,表示其功能。裝置完全以「裝置單位」運作,由其開發人員決定:它們可以是畫素、大點(1/72 英吋)、特威普……,並且可以在「x」和「y」方向上有所不同16

下一層是圖形「引擎」,它是與裝置的主要介面(儘管圖形子系統會直接與裝置對話)。這負責裁剪線條、矩形和多邊形,將 pch0...26 轉換為線條/圓形組,置中(以及調整)文字、呈現數學運算式(「繪製數學」)以及將顏色描述(例如名稱)對應至內部表示。

引擎的另一個功能是管理顯示清單和快照。有些圖形裝置執行個體(但並非全部)會維護顯示清單,這是對裝置執行的一系列「清單」操作,以產生目前的繪製(自從裝置開啟或繪製上次清除以來,例如透過 plot.new)。螢幕裝置通常會維護顯示清單以處理重新繪製和調整大小的事件,而基於檔案的格式則不會—顯示清單也會用於實作 dev.copy() 及相關函式。顯示清單是 .Internal(基本圖形)或 .Call.graphics(格線圖形)呼叫的配對清單,這表示實作圖形操作的 C 程式碼會在顯示清單重新播放時再次呼叫:除了記錄操作(如果成功)的部分。

目前的圖形狀態快照是由 GEcreateSnapshot 擷取,並在稍後於會話中由 GEplaySnapshot 重新播放。這些由 recordPlot()replayPlot()windows() 裝置的 GUI 選單使用。這個「狀態」包含顯示清單。

最上層包含圖形子系統。儘管自 2001 年以來已提供 24 個子系統,但目前僅存在兩個子系統,即「基本」和「網格」。在初始化 R 時,基本子系統會向引擎註冊,而在關閉 R 會話時會註銷(透過 KillAllDevices)。網格子系統會在 .onLoad 函數中註冊,並在 .onUnload 函數中註銷。圖形子系統也可能將「狀態」資訊儲存在快照中(目前基本子系統會儲存,而網格子系統不會)。

套件 grDevices 最初是為了包含基本圖形裝置而建立的(儘管 X11 因為它帶入的外部程式庫數量而位於獨立的隨選載入模組中)。從那時起,它已被用於其他被認為可與 grid 搭配使用的功能,因此已從套件 graphics 轉移到 grDevices。這主要與處理色彩以及記錄和重播繪圖有關。


6.1 圖形裝置

R 附帶多個圖形裝置,並且支援第三方套件提供額外的裝置,現在有幾個套件會這麼做。本節從圖形裝置撰寫者的角度描述裝置內部結構。


6.1.1 裝置結構

內部使用兩種型態,它們是指向與圖形裝置相關的結構的指標。

類型 DevDesc 是一個結構,定義在標頭檔案 R_ext/GraphicsDevice.h(它包含在 R_ext/GraphicsEngine.h 中)。這描述了裝置的物理特性、裝置驅動程式的功能,並包含一組回呼函式,圖形引擎將使用這些函式來取得裝置的資訊並啟動動作(例如,新頁面、繪製線條或一些文字)。類型 pDevDesc 是指向此類型的指標。

在適當的情況下,可以省略下列回呼函式(或將它們設定為空指標,這是它們的預設值),圖形引擎將採取預設行為:activatecapdeactivatelocatorholdflush(API 版本 9)、modenewFrameConfirmpathrastersize

裝置單位與物理維度之間的關係是由 DevDesc 結構的元素 ipr 設定的:長度為 2 的「double」陣列。

類型 GEDevDesc 是一個結構,定義在 R_ext/GraphicsEngine.h(檔案中有註解)中,如下所示

typedef struct _GEDevDesc GEDevDesc;
struct _GEDevDesc {
    pDevDesc dev;
    Rboolean displayListOn;
    SEXP displayList;
    SEXP DLlastElt;
    SEXP savedSnapshot;
    Rboolean dirty;
    Rboolean recordGraphics;
    GESystemDesc *gesd[MAX_GRAPHICS_SYSTEMS];
    Rboolean ask;
}

因此,這基本上是一個裝置結構,加上圖形引擎維護的裝置資訊,且通常17引擎可見,但裝置不可見。類型 pGEDevDesc 是指向此類型的指標。

圖形引擎維護一個裝置陣列,作為 GEDevDesc 結構的指標。陣列大小為 64,但第一個元素總是 "null 裝置" 佔用,最後一個元素則作為哨兵保持為 NULL。18 此陣列反映在 R 變數 ‘.Devices’ 中。裝置被終止後,其元素可供重新配置(其名稱將在 ‘.Devices’ 中顯示為 "")。裝置中只有一個是「作用中」:如果沒有其他裝置已開啟且未終止,則為 null 裝置。

圖形裝置的每個執行個體需要透過與下列程式碼非常類似的程式碼設定 GEDevDesc 結構

    pGEDevDesc gdd;

    R_GE_checkVersionOrDie(R_GE_version);
    R_CheckDeviceAvailable();
    BEGIN_SUSPEND_INTERRUPTS {
        pDevDesc dev;
        /* Allocate and initialize the device driver data */
        if (!(dev = (pDevDesc) calloc(1, sizeof(DevDesc))))
            return 0; /* or error() */
        /* set up device driver or free ‘dev’ and error() */
        gdd = GEcreateDevDesc(dev);
        GEaddDevice2(gdd, "dev_name");
    } END_SUSPEND_INTERRUPTS;

DevDesc 結構包含 void * 指標 ‘deviceSpecific’,用於儲存裝置特定的資料。設定裝置驅動程式包括初始化 DevDesc 結構的所有非零元素。

請注意,裝置結構在配置時會歸零:這提供了一些針對結構未來擴充的保護,因為圖形引擎可以新增需要非 NULL/非零才能「開啟」的元素(結構以 64 個保留位元組結尾,這些位元組將歸零並允許未來擴充)。

引擎/裝置 API 的版本號碼提供了更多保護,R_GE_version 定義在 R_ext/GraphicsEngine.h 中,以及存取函式

int R_GE_getVersion(void);
void R_GE_checkVersionOrDie(int version);

如果圖形裝置呼叫 R_GE_checkVersionOrDie(R_GE_version),它可以確保只會在提供其設計和編譯時所針對的 API 版本的 R 中使用。

DevDesc 結構還包含 intdeviceVersion’,用於指出裝置支援的引擎/裝置 API 版本。如果裝置驅動程式正確設定此版本,則裝置驅動程式不需要使用 R_GE_checkVersionOrDie(R_GE_version),因為圖形引擎不會使用高於裝置支援版本的 API 回呼。


6.1.2 裝置功能

下列「功能」可定義為裝置的 DevDesc 結構。

  • canChangeGammaRboolean:顯示器伽瑪值可以調整嗎?由於已移除伽瑪支援,此選項現已忽略。
  • canHadjinteger:裝置是否能透過 text 回呼函數,進行文字的水平調整?如果可以,其精確度為何?0 = 無調整,1 = {0, 0.5, 1}(靠左、置中、靠右對齊)或 2 = 在靠左和靠右對齊之間連續變動([0,1])。
  • canGenMouseDownRboolean:裝置是否能處理滑鼠按下事件?此旗標和後面的三個旗標目前並未由 R 使用,但為了向後相容性而保留。
  • canGenMouseMoveRboolean:滑鼠移動事件同前。
  • canGenMouseUpRboolean:滑鼠放開事件同前。
  • canGenKeybdRboolean:鍵盤事件同前。
  • hasTextUTF8Rboolean:非符號文字是否應以 (UTF-8) 傳送至 textUTF8strWidthUTF8 回呼函數,並以 Unicode 點數(負值)傳送至 metricInfo 回呼函數?
  • wantSymbolUTF8Rboolean:符號文字是否應與其他文字以相同方式以 UTF-8 處理?需要 textUTF8 = TRUE
  • haveTransparency:裝置是否支援半透明色彩?
  • haveTransparentBg:背景是否可以完全或半透明?
  • haveRaster:是否支援呈現光柵影像?
  • haveCapture:是否支援 grid::grid.cap
  • haveLocator:是否有互動定位器?
  • deviceClip:引擎是否應將所有剪裁交給裝置?

haveRasterhaveCapturehaveLocator 通常可以從對應函式中存在 NULL 項目而非存在對應函式推論為 false。

此外,capabilities 回呼允許裝置驅動程式提供更詳細的資訊,特別是與引擎/裝置 API 版本 13 或更高的回呼相關的資訊。

capabilities 回呼會呼叫一串整數向量,這些向量代表圖形引擎基於 DevDesc 結構中的旗標和「deviceVersion」所做的最佳猜測。對於某些功能,整數向量長度為 1,其中 0 表示不支援、1 表示支援,或 NA 表示支援未知。對於支援可能較為細微的功能,整數向量可能會取較高的值或長度大於 1,不過長度 1 和 0 仍然表示不支援,而 NA 仍然表示支援未知。

此清單中的下列元件可能需要修改(對於這些元件,如果「deviceVersion」太低,圖形引擎只能猜測 0,否則猜測 NA

  • patterns 元件報告支援何種圖樣填滿。如果裝置支援一種或多種圖樣類型,此元件應替換為包含每個支援圖樣類型值的整數向量;圖形引擎提供常數 R_GE_linearGradientPatternR_GE_radialGradientPatternR_GE_tilingPattern。如果裝置不提供支援,此元件應設定為 0。
  • clippingPaths 元件報告是否支援任意剪裁路徑。如果裝置支援剪裁路徑,此元件應設定為 1。如果裝置不提供支援,此元件應設定為 0。
  • masks 元件報告支援何種遮罩。如果裝置支援一種或多種遮罩類型,此元件應替換為包含每個支援遮罩類型值的整數向量;圖形引擎提供常數 R_GE_alphaMaskR_GE_luminanceMask。如果裝置不提供支援,此元件應設定為 0。
  • compositing 元件報告支援哪些合成運算子。如果裝置支援一種或多種合成運算子,此元件應替換為包含每個支援運算子值的整數向量;可能的運算子清單很長,包含 Porter-Duff 運算子和 Adobe PDF 混合模式;圖形引擎提供常數 R_GE_compositeClear 等。如果裝置不提供支援,此元件應設定為 0。
  • transformations 元件報告是否支援仿射轉換。如果裝置支援轉換,此元件應設定為 1。如果裝置不提供支援,此元件應設定為 0。
  • 元件 paths 會回報是否支援由多個形狀組成的路徑描邊和填滿。如果裝置支援描邊和填滿路徑,此元件應設定為 1。如果裝置不提供支援,此元件應設定為 0。
  • 元件 glyphs 會回報是否支援呈現字形(例如透過 grid::grid.glyph())。如果裝置支援呈現字形,此元件應設定為 1。如果裝置不提供支援,此元件應設定為 0。

圖形引擎提供常數,例如 R_GE_capability_patterns,用於選取功能清單中適當的元件。

裝置驅動程式傳回未變更的功能清單是有效的(如果沒有幫助)。


6.1.3 處理文字

處理文字可能是圖形裝置最困難的任務,而設計允許裝置選擇性地指出它有額外的功能。(如果裝置沒有,這些功能如果可能的話會在圖形引擎中處理。)

所有圖形裝置中必須有的三個處理文字的回呼函式是 textstrWidthmetricInfo,其宣告如下

void text(double x, double y, const char *str, double rot, double hadj,
          pGgcontext gc, pDevDesc dd);

double strWidth(const char *str, pGEcontext gc, pDevDesc dd);

void metricInfo(int c, pGEcontext gc,
               double* ascent, double* descent, double* width,
               pDevDesc dd);

gc」參數提供圖形內容,最重要的部分是目前的字型和字型大小,「dd」是指向活動裝置結構的指標。

text 回呼函數應在「str」的「(x, y)19 處繪製圖形,並以「rot」度逆時針旋轉。(有關「hadj」,請見下方。)對於橫向文字,其詮釋為基準線位於 y,而起點為 x,因此第一個字元的任何左軸承都將從 x 開始。

strWidth 回呼函數計算字串在以目前字型橫向繪製時所佔用的寬度。(此處的寬度預期包含(最好)或不包含左軸承和右軸承。)

metricInfo 回呼函數計算單一字元的尺寸:ascent 是其超出基準線的距離,而 descent 是其超出基準線的距離。width 是放置字元時游標應前進的量。對於 ascentdescent,這應為字形所放置「墨水」的邊界框,而非組裝傳統文字行時可能使用的框(例如,這需要讓 hat(beta) 能正確運作)。不過,width 在 plotmath 中用於前進至下一個字元,因此需要包含左軸承和右軸承。

解譯」' c' 取決於區域設定。在單位元組區域設定中,值 32...255 指出區域設定中對應的字元(如果存在)。對於符號字型(如「graphics::par(font=5)」、「grid::gpar(fontface=5」和「plotmath」所使用),值 32...126, 161...239, 241...254 指出 Adobe Symbol 編碼中的字形。在多位元組區域設定中,c 代表 Unicode 點(符號字型除外)。因此函式需要包含類似下列的程式碼

    Rboolean Unicode = mbcslocale && (gc->fontface != 5);
    if (c < 0) { Unicode = TRUE; c = -c; }
    if(Unicode) UniCharMetric(c, ...); else CharMetric(c, ...);

此外,如果裝置功能 hasTextUTF8(請參閱下方)為 true,Unicode 點將傳遞為負值:上述程式碼片段顯示如何處理此情況。(這僅適用於裝置功能 wantSymbolUTF8 為 true 時的符號字型。)

如果可行,圖形裝置應處理文字裁切。它會透過結構元素 canClip 指出這一點,如果為 true,將呼叫回呼 clip 來設定裁切區域。如果未執行此動作,引擎將會非常粗糙地裁切(省略裁切區域外側的所有文字)。

裝置結構具有整數元素 canHadj,指出裝置是否能執行文字的水平對齊。如果為一,text 的引數「hadj」將呼叫為 0 ,0.5, 1,以指出在指定位置的左對齊、置中和右對齊。如果為二,則假設支援 [0, 1] 範圍內的連續值。

功能 hasTextUTF8 如果為 true,會有兩個結果。首先,有回呼 textUTF8strWidthUTF8,它們的行為應與 textstrWidth 相同,但假設 ‘str’ 是 UTF-8 編碼,而不是目前區域設定的編碼。圖形引擎會對所有文字呼叫這些,符號字型除外。其次,Unicode 點會以負整數傳遞給 metricInfo 回呼。如果您的裝置偏好有 UTF-8 編碼符號,請定義 wantSymbolUTF8hasTextUTF8。在這種情況下,符號字型的文字會傳送給 textUTF8strWidthUTF8

有些裝置可以產生高品質的旋轉文字,但基於位圖的裝置通常不行。可以產生高品質旋轉文字的裝置應將 useRotatedTextInContour 設為 true,從圖形 API 版本 4 開始。

還有其他幾個元素與圖形引擎精確放置文字有關

double xCharOffset;
double yCharOffset;
double yLineBias;
double cra[2];

這些元素有點神秘。元素 cra 提供字元大小的指示,par("cra") 在基礎圖形中,以裝置單位表示。神秘的是「字元大小」的意思是什麼:哪個字元、哪個字型、哪個大小?可以透過檢視這項功能的用途來獲得一些幫助。第一個元素「寬度」,R 沒有使用,只用來設定圖形參數。第二個元素「高度」,用來設定行距,也就是 par("mar")par("mai") 等等之間的關係。建議選擇

dd->cra[0] = 0.9 * fnsize;
dd->cra[1] = 1.2 * fnsize;

其中「fnsize」是裝置上標準字型(cex=1)的「大小」,以裝置單位表示。因此,對於 12 點字型(圖形裝置的常用預設值),「fnsize」應為 12 點,以裝置單位表示。

其餘元素更為神秘。postscript() 裝置說明

    /* Character Addressing Offsets */
    /* These offsets should center a single */
    /* plotting character over the plotting point. */
    /* Pure guesswork and eyeballing ... */

    dd->xCharOffset =  0.4900;
    dd->yCharOffset =  0.3333;
    dd->yLineBias = 0.2;

看來 xCharOffset 目前未使用,而 yCharOffset 則由基本圖形系統用於在指定 pos 時設定 text() 中的垂直對齊,以及在 identify() 中。圖形引擎偶爾會在嘗試精確置中文字時使用它,例如 points()grid.points()pch 的字串值,但僅在沒有精確字元度量資訊或多行字串時才會使用。

yLineBias 用於基本圖形系統中的 axis()mtext(),以提供其「padj」引數的預設值。

R_GE_version 16 (R_GE_glyphs) 開始,還有一個 glyph 回呼。

    void glyph(int n, int *glyphs, double *x, double *y, 
               SEXP font, double size,
               int colour, double rot, pDevDesc dd);

這指示裝置在給定字型中繪製特定字形,其中字型由檔名(和索引)指定,字型系列、粗細和樣式也作為備用提供。


6.1.4 慣例

目標是讓圖形裝置的(預設)輸出盡可能相似。一般來說,人們會遵循 postscriptpdf 裝置的模型(它們共用大部分的內部程式碼)。

以下慣例已建立

  • 裝置的預設大小應為 7 英吋見方。
  • 應該有一個預設為 12 的「點大小」引數,並且它應提供大點(1/72 英吋)的點大小。如何準確地詮釋這一點取決於字型,但它應使用一個字型,該字型可與間距為 1/6 英吋的線條搭配使用,並且與間距為 1/5 英吋的線條搭配使用時看起來不錯(即 2 點前導)。
  • 預設字型系列應為無襯線字型,例如 Helvetica 或類似字型(例如 Windows 上的 Arial)。
  • lwd = 1 應對應於 1/96 英吋的線條寬度。這將會對基於像素的裝置造成問題,而且通常會有 1 個像素的最小線條寬度(儘管在使用線條反鋸齒時這可能不適當,且 cairo 偏好最小 2 個像素)。
  • 即使非常小的圓圈也應可見,例如使用最小半徑 1 個像素或以單個填滿像素取代非常小的圓圈。
  • 應記錄 RGB 色彩值將如何詮釋,且最好是 sRGB。
  • 說明頁面應說明其對這些慣例的政策。

對於位圖裝置而言,這些慣例較不清晰,特別是在位圖格式沒有設計解析度的情況下。

線條紋理 (par("lty") 的詮釋說明在標頭檔 GraphicsEngine.hpar 的說明中:請注意圖樣的「比例」應與線條寬度成正比(至少對於大於預設值的寬度)。


6.1.5 「模式」

其中一個裝置回呼函數是 mode,在標頭檔中說明如下

     * device_Mode is called whenever the graphics engine
     * starts drawing (mode=1) or stops drawing (mode=0)
     * GMode (in graphics.c) also says that
     * mode = 2 (graphical input on) exists.
     * The device is not required to do anything

因為 mode = 2 最近才在裝置層級中說明。它可用於變更圖形游標,但目前裝置會在 locator 回呼函數中執行此動作。(在基本圖形中,模式會設定為 locator 呼叫的期間,但如果 type != "n" 會在註解執行期間針對每個點切換回去。)

許多裝置確實不會對此呼叫執行任何動作,但有些螢幕裝置會確保在以 mode = 0 呼叫時將繪圖沖洗到螢幕。很令人想用於某種緩衝,但請注意「繪圖」會在很低的層級中詮釋,而典型的單一數字會停止並開始繪製許多次。在 X11() 裝置中引入的緩衝會使用 mode = 0 來表示活動:它會在 100 毫秒的非活動時間後更新螢幕。

如果此回呼函數沒有執行任何動作,就不需要提供。


6.1.6 圖形事件

圖形裝置可以設計成處理使用者互動:並非所有裝置都能處理。

使用者可以使用 grDevices::setGraphicsEventEnv 來設定裝置驅動程式中的 eventEnv 環境,以容納事件處理常式。當使用者呼叫 grDevices::getGraphicsEvent 時,R 會執行三步驟。首先,它會將裝置驅動程式成員 gettingEvent 設定為 true,針對每個具有非 NULL eventEnv 項目的裝置,並在定義回呼時呼叫 initEvent(dd, true)。然後它會進入事件迴圈。每次執行迴圈時,R 會處理事件一次,然後檢查是否有任何裝置已將 eventEnvresult 成員設定為非 NULL 值,並會儲存找到的第一個值以回傳。提供 C 函式 doMouseEventdoKeybd 來呼叫 R 事件處理常式 onMouseDownonMouseMoveonMouseUponKeybd,並在此步驟中設定 eventEnv$result。最後,再次呼叫 initEvent,並將 init=false 告知裝置迴圈已完成,並將結果回傳給使用者。


6.1.7 特定裝置

特定裝置大多在它們的來源中以註解記錄,儘管對於多年歷史的裝置,那些註解可能需要更新。本小節是設計決策筆記的儲存庫。


6.1.7.1 X11()

X11(type="Xlib") 裝置可追溯到 1990 年代中期,當時是用最基本的 X11 工具組 Xlib 編寫的。從那時起,它可以選擇使用其他工具組的一些功能:libXt 用於讀取 X11 資源,而 libXmu 用於處理剪貼簿選取。

使用基本的 Xlib 程式碼可以快速繪製,但有其限制。它不支援半透明色彩(出現在 2000 年的 Xrender 工具組中),也不支援旋轉文字(R 透過將文字呈現在位圖並旋轉後者來實作)。

X11 視窗的提示要求使用後備儲存,而一些視窗管理員可能會使用它來處理重新繪製,但看來大多數重新繪製都是透過重播顯示清單來完成的(而這裡的快速繪製非常有幫助)。

尋找字型時會出現長久以來的問題。許多使用者並未意識到字型是 X 伺服器的功能,而非 R 執行的機器。經過許多困難後,R 會先嘗試在標準 75dpi 和 100dpi X11 字型套件中提供的 Adobe 字型大小中,尋找最接近的大小相符項,即使接近 100dpi 螢幕的使用者僅安裝 75dpi 設定集,這也會無法運作。75dpi 設定集允許在 100dpi 螢幕上縮小至 6 點,但有些使用者確實會嘗試使用較小的尺寸,甚至 6 點和 8 點點陣字型看起來也不好看。

UTF-8 地區設定的導入造成了另一波困難。X11 只有極少數的真正 UTF-8 字型,並為 iso10646-1 編碼產生複合字型集。不幸的是,除了少數幾個大小的等寬字型(不適合圖形註解)之外,這些字型集的涵蓋範圍似乎很低,而且在字形遺失的情況下,繪製的內容通常相當不令人滿意。

目前的作法是利用更現代的工具組,即用於繪製的 cairo 和用於字型管理的 Pango,由於這些工具組與 Gtk+2 相關,因此廣泛可用。Cairo 支援半透明色彩和 Alpha 混合(透過 Xrender),以及線條和文字顯示的反鋸齒。Pango 的字型管理基於 fontconfig,有點神秘,但它似乎主要是在執行 R 的機器上使用 Type 1 和 TrueType 字型,並將灰階點陣圖傳送給 cairo。


6.1.7.2 windows()

windows() 裝置是一系列裝置:它支援繪製至 Windows(增強)圖形檔案、BMPJPEGPNGTIFF 檔案,以及 Windows 印表機。

在這些情況下,大部分的主要繪製都針對點陣圖:這用於(預設)螢幕裝置的緩衝,這也讓目前的繪製可以儲存為 BMP、JPEG、PNG 或 TIFF(它會將內部點陣圖以適當的格式複製到檔案中)。

裝置單位為像素(圖形檔裝置上的邏輯單位)。

此程式碼最初由 Guido Masarotto 編寫,大量使用巨集,這可能會讓程式碼難以解開。

對於螢幕裝置,xd->gawin 是螢幕的畫布,而 xd->bm 是螢幕外的位圖。因此巨集 DRAW 會安排繪製到 xd->bm,如果關閉緩衝,也會繪製到 xd->gawin。對於所有其他裝置,xd->gawin 是畫布,是 jpeg()png() 裝置的位圖,以及 win.metafile()win.print 裝置的 Windows 圖形檔內部表示。由於「繪製」是由 Windows GDI 呼叫到適當的畫布來完成,因此其精確性質會被 GDI 系統隱藏。

螢幕裝置上的緩衝是透過執行計時器來達成,當計時器觸發時,會將內部位圖複製到螢幕上。這會設定為每 500 毫秒觸發一次(預設值),並在繪製活動後重設為 100 毫秒。

重新繪製事件會透過將內部位圖複製到螢幕畫布(然後重新初始化計時器)來處理,除非有調整大小。調整大小會透過重新播放顯示清單來處理:如果使用具有捲軸的固定畫布,這可能不是必要的,但這是三種調整大小形式中較不常用的。

裝置上的文字在近年已移至「Unicode」(UCS-2)。對於標準文字會要求 UTF-8(hasTextUTF8 = TRUE),並在檔案 src/extra/graphapp/gdraw.c 中的繪製函數中轉換為 UCS-2。不過,GDI 不支援 Unicode 符號字型,而符號會以 Adobe Symbol 編碼處理。

螢幕裝置和位圖裝置上引入了對半透明色彩(alpha 通道介於 0 和 255 之間)的支援。20這是透過在色彩的不透明版本中繪製另一個內部位圖 xd->bm2,然後將該位圖與 xd->bm 進行 alpha 混合來完成的。alpha 混合常式在一個獨立的 DLL msimg32.dll 中,在第一次使用時載入。會儘可能 alpha 混合一個較小的矩形區域(這是程式碼中的矩形 r),但像斜接線條之類的東西會讓線條和多邊形邊界的緊密邊界框估計工作量過大。半透明色彩的線條並不多見,而效能似乎是可以接受的。

png() 中支援透明背景早於 libpng 中的完整 alpha 通道支援(更別提 PNG 檢視器了),因此利用了 PNG 早期版本中有限的透明度支援。在使用 24 位色彩時,這是透過標記一個要顯示為透明的單一色彩來完成的。R 選擇了「#fdfefd」,並將其用作背景色彩(如果指定的背景色彩是透明的,則在 GA_NewPage 中使用(而且所有非不透明背景色彩都視為透明)。因此,這是透過在 PNG 檔案中標記該色彩來運作,而沒有透明度支援的檢視器會看到一個略微偏白的背景,就好像有一個近乎白色的畫布一樣。如果在 PNG 檔案中使用了調色盤(如果使用的色彩少於 256 種),則會將此色彩記錄為完全透明,而將其餘色彩記錄為不透明。如果可以使用 32 位色彩,我們就可以新增一個完整的 alpha 通道,但這取決於繪圖硬體和 GDI 的未記錄屬性。


6.2 色彩

裝置接收色彩作為 typedef rcolor(在標頭 R_ext/GraphicsEngine.h 中定義的 unsigned int)。4 個位元組依序為 RGBalpha,從最低位元組到最高位元組。因此,RGB 各有 256 個亮度等級,範圍從 0 到 255。alpha 位元組代表不透明度,因此值 255 為完全不透明,而 0 為完全透明:許多裝置(但並非全部)會處理半透明色彩。

色彩可以在 C 中透過巨集 R_RGBA 建立,且在 R_ext/GraphicsDevice.h 中定義了一組巨集來萃取各種組成部分。

基本圖形系統中的色彩最初採用自 S(以及之前貝爾實驗室的 GRZ 函式庫),概念為一個色彩調色盤(大小可變),由數字「1...N」加上「0」(目前裝置的背景色彩)作為參考。R 引入了透過字元串來參考色彩的概念,形式為「#RRGGBB」或「#RRGGBBAA」(以十六進位表示位元組),如函式 rgb() 所提供,或透過名稱:657 個已知名稱載於字元向量 colors 中,以及套件 grDevices 中檔案 colors.c 的表格中。請注意,半透明色彩並非「預先乘以」,因此 50% 透明的白色為「#ffffff80」。

整數或字元 NA 色彩在內部會對應到透明的白色,字元串 "NA" 也是如此。

負色彩數字為錯誤。大於「N」的色彩會環繞,因此例如在大小為 8 的預設調色盤中,色彩「10」在調色盤中為色彩「2」。

整數色彩的使用範圍比基本繪圖子系統更廣泛,因為它們受到套件 grid 的支援,因此也受到 latticeggplot2 的支援。(套件 rgl 也使用它們。)grid 重新定義了色彩「0」為透明白色,但 rgl 使用了 col2rgb,因此採用了基本繪圖的背景色彩。

請注意,正整數色彩指的是目前的色盤,而色彩「0」指的是目前的裝置(如果需要,會開啟一個裝置)。這些在使用時會對應到 rcolor 類型:這在重新播放顯示清單時很重要,例如當裝置調整大小或使用 dev.copy 時。色盤應被視為每個工作階段:它儲存在套件 grDevices 中。

慣例是裝置使用色彩空間「sRGB」。這是一個產業標準:它被 Web 瀏覽器和所有數位相機(除了高階相機)的 JPEG 使用。詮釋是繪圖裝置和處理色彩的程式碼的事,而不是繪圖引擎或子系統的事。

R 使用類似於 PostScript 和 PDF 的繪製模型。這表示當形狀(圓形、矩形和多邊形)可以同時填滿和描邊時,應先填滿再描邊(否則只會看到一半的邊線)。當填滿和邊線都是半透明時,在詮釋意圖方面會有一些空間。大多數裝置會先填滿再描邊,在每個步驟中進行 Alpha 混合。然而,PDF 會自動將物件分組,而且 當填滿和邊線具有相同的 Alpha 值時,它們會繪製在同一層上,然後在一個步驟中進行 Alpha 混合。(請參閱 PDF 參考手冊第六版第 569 頁,版本 1.7。很遺憾的是,儘管這是 PDF 標準中所述應發生的情況,但有些檢視器並未正確實作。)

從顏色數字對應到類型 rcolor 的對應主要是由函數 RGBpar3 執行:這會從 R 二進位檔匯出,但連結到套件 grDevices 中的程式碼。第一個參數是指向字元、整數或雙精度向量的 SEXP,第二個是顏色 0(或 "0")的 rcolor 值。C 進入點 RGBpar 是將 0 視為透明白色的一個包裝函數:它通常用於設定裝置的顏色預設值。R 層級的包裝函數是 col2rgb

另外還有 R_GE_str2col,它會取得一個 C 字串並轉換成類型 rcolor"0' 會轉換成透明白色。

有一個 R 層級的顏色轉換為 ‘##RRGGBBAA’ 的函數 image.default(useRaster = TRUE)

API 中另一個顏色轉換進入點是 name2col,它會取得一個顏色名稱(一個 C 字串)並傳回一個類型為 rcolor 的值。這會處理 "NA""transparent" 和 R 函數 colors() 已知的 657 種顏色。


6.3 基礎圖形

基礎圖形系統已在 R 3.0.0 中移轉到套件 graphics:它之前是在 src/main 中的檔案中實作。

由於歷史原因,它在很大程度上分為兩層實作。檔案 plot.cplot3d.cpar.c 包含實作基本圖形操作的約 30 個 .External 呼叫的程式碼。然後,此程式碼會呼叫名稱以 G 開頭且在檔案 graphics.c 中的標頭 Rgraphics.h 中宣告的函式,而這些函式又會呼叫圖形引擎(其函式幾乎都以 GE 開頭)。

基礎圖形子系統基礎架構的很大一部分是圖形參數(由 par() 設定/讀取)。這些儲存在私有標頭 Graphics.h 中宣告的 GPar 結構中。此結構有兩個變數(statevalid)追蹤裝置上基礎子系統的狀態,以及許多記錄圖形參數及其函式的變數。

基礎系統狀態包含在私有標頭 GraphicsBase.h 中定義的 baseSystemState 結構中。這包含三個 GPar 結構和一個用於記錄 plot.new()(或 persp)是否已成功在裝置上使用的布林變數。

三個 GPar 結構的複本用於儲存目前的參數(透過 gpptr 存取)、「裝置複本」(透過 dpptr 存取)以及「裝置複本」參數的儲存複本空間。目前的參數很明顯是目前正在使用的參數,並且會在呼叫 plot.new() 時從「裝置複本」複製(無論是否進展到下一個「頁面」)。儲存的複本會保留裝置上次完全清除時的狀態(例如,當 plot.new() 呼叫 par(new=TRUE) 時),並用於重播顯示清單。

分離並非完全乾淨:如果透過 plot.window() 設定具有對數比例的繪圖,則「裝置副本」會被變更。

graphics.cstatic 變數中,還有大部分圖形參數的另一個副本,用於在處理高層級圖形呼叫中的內嵌參數時,保留目前的參數(由 ProcessInlinePars 處理)。

基本子系統的快照會記錄 GPar 結構的「已儲存裝置副本」。


6.3.1 引數和參數

某些圖形參數(由 par 設定)與同名基本圖形函數的引數之間存在令人遺憾的混淆。此說明可能有助於釐清記錄。

大部分高層級繪圖函數接受圖形參數作為額外的引數,如果尚未命名引數(這是混淆的主要來源),則通常會傳遞給較低層級的函數。

圖形參數 bg 是繪圖的背景顏色。引數 bg 指的是填滿符號 2125 的填滿顏色。它是 plot.xy 函數的引數,但通常由 points 的預設方法傳遞,通常來自 plot 方法。

圖形參數 cexcolltylwdpch 也會顯示為 plot.xy 的引數,因此通常會從較高層級的繪製函數(例如 linespointsplot 方法)傳遞為引數。它們會顯示為 legend 的引數,colltylwdarrowssegments 的引數。當用作引數時,它們可以是向量,可重複使用以控制各種線條、點和線段。當設定為圖形參數時,它們會設定預設的呈現:此外,par(cex=) 會設定後續呼叫(作為引數或線上圖形參數)會相乘的整體字元擴充。

在兩種使用類別中,遺失值處理方式不同。一般來說,在 par 中使用時,這些是錯誤,但在用作向量引數的元素時,會導致遺漏繪製的對應元素。原本引數的詮釋主要交給裝置,但現在圖形引擎會先處理其中一些部分(但例如 lwd = 0 的處理方式仍然因裝置而異,有些會將其詮釋為「最細的可能」線條)。


6.4 格線圖形

[至少有文件說明的指標。]


7 GUI 主控台

標準 R 前端是在終端機中執行的程式,但有數種方法可提供 GUI 主控台。

這可透過從基於終端機的 R 載入的套件來完成,並在啟動碼中啟動主控台,或由使用者執行特定函數:套件 Rcmdr 是個著名的範例,具有基於 Tk 的 GUI。

過去有一個由 R --gui=GNOME 呼叫的基於 Gtk 的主控台:這仰賴於前端 shell 腳本中的特殊情況,以啟動不同的可執行檔。仍有 R --gui=Tk,它會啟動基於終端機的 R,並在修改的啟動順序中執行 tcltk::tkStartGui()

然而,執行 GUI 主控台的主要方法是啟動執行嵌入式 R 的獨立程式:這可透過 Windows 上的 Rgui.exe 和 macOS 上的 R.app 來完成。前者是 R 的組成部分,而主控台的程式碼目前在 R.dll 中。


7.1 R.app

R.app 是提供主控台的 macOS 應用程式。其來源是一個獨立的專案21,其二進位檔連結到 R 安裝,它以動態函式庫 libR.dylib 執行 R 安裝。macOS 的標準 CRAN R 發行版會將 GUI 和 R 本身綑綁在一起,但安裝 GUI 是可選的,而且每個元件都可以個別更新。

R.app 依賴於 libR.dylib 存在於特定位置,因此 R 必須以 Mac macOS「架構」形式建置並安裝。特別是,它使用 /Library/Frameworks/R.framework/R。由於架構可能包含多個 R 版本,因此這是一個符號連結。它最終解析為 /Library/Frameworks/R.framework/Versions/Current/Resources/lib/libR.dylib,這是(在 CRAN 發行版中)包含多個子架構的「胖」二進位檔案。

macOS 應用程式是目錄樹:每個 R.app 都包含一個以 Objective-C 編寫的前端,適用於一個子架構:在標準發行版中,32 位元和 64 位元 Intel 架構有獨立的應用程式。

最初,R 原始碼包含大量僅供 macOS GUI 使用的程式碼,但已移轉到 R.app 原始碼。

R.app 以嵌入式應用程式的形式啟動 R,其命令列包含 --gui=aqua(請見下方)。它使用標頭 Rinterface.h 中定義的大部分介面指標,加上檔案 src/main/sysutils.c 中的私有介面指標。它會將一個名為 tools:RGUI 的環境新增到搜尋路徑的第二個位置。這包含許多用於支援選單項目的公用程式函數,例如 package.manager(),以及遮罩套件 base 中函數 q()quit() 的函數,自訂版本會以特定於 R.app 的方式儲存記錄。

R 有 configure 選項 --with-aqua,用於自訂 R 的建置方式:這與 --enable-R-framework 選項不同,後者會導致 make install 將 R 安裝為 R.app 所需的架構。(--with-aqua 選項是 macOS 上的預設值。)它會在 config.h 中設定巨集 HAVE_AQUA 和 make 變數 BUILD_AQUA_TRUE。這些會產生幾個後果

  • 裝置 quartz() 建立(除了作為 stub)於套件 grDevices 中:這需要一個 Objective-C 編譯器。然後 quartz() 可用於終端機 R,只要後者可存取 macOS 畫面。
  • 檔案 src/unix/aqua.c 已編譯。這現在只包含 quartz() 裝置的介面指標。
  • capabilities("aqua") 已設定為 TRUE
  • 個人函式庫目錄的預設路徑設定為 ~/Library/R/arch/x.y/library
  • 支援在等待 system() 回傳時設定「忙碌」指示器。
  • R_ProcessEvents 在套件 parallel 的分岔子行程中受到抑制。在 R.app 中的關聯回呼執行不應在子行程中執行的動作,而分岔會分岔整個程序,包括主控台。
  • 支援使用選項 --gui=aqua 啟動嵌入式 R:執行此動作時,全域 C 變數 useaqua 會設定為 true 值。這會產生後果
    • R 會期透過 R_Interactive 宣告為互動式。
    • .Platform$GUI 設定為 "AQUA"。這會產生後果
      • 環境變數 DISPLAY 設定為「:0」,如果尚未設定。
      • /usr/local/bin 附加至 PATH,因為這是 gfortran 安裝的位置。
      • 預設 HTML 瀏覽器切換為 R.app 中的瀏覽器。
      • 各種小工具已切換至 R.app 中提供的版本:這些包括圖形選單、資料編輯器(但不是 View() 使用的資料檢視器)以及由 browseEnv() 呼叫的工作區瀏覽器。
      • 載入 grDevices 套件時,它知道它是在 R.app 下執行,因此會通知任何 quartz 裝置,Quartz 事件迴圈已經在執行中。
    • 使用作業系統的 system 函數(包括 system()system2(),以及啟動編輯器和分頁器)已由 R.app 中的版本取代(預設情況下,它只會呼叫作業系統的 system,並重設各種訊號處理常式)。
  • 如果 R 是由 --gui=aqua 啟動,或 R 在不是 'dumb' 類型的終端機中執行,則標準輸出至檔案 stdoutstderr 會透過 C 函數 Rstd_WriteConsoleEx 導向。這會使用 ANSI 終端機逸出字元,將傳送至 stderr 的列印成粗體顯示在 stdout 上。
  • 基於歷史原因,啟動選項 -psn 允許使用,但會略過。(似乎在 2003 年的 'r27492' 中,這是由 Finder 新增的。)

8 工具

可以透過各種命令列引數和環境變數來控制 R CMD check 的行為。

有一個內部 --install=value 命令列引數未顯示在 R CMD check --help 中,可能的數值為

check:file

假設安裝已執行完畢,stdout/stderr 為 file,其內容需要檢查(不重複安裝)。這對儲存庫維護者套用的檢查很有用:它會縮短檢查時間,因為安裝時間已包含在套件已安裝的條件中。在這種情況下,也需要使用命令列選項 --library 指定套件安裝的 位置

fake

偽造安裝,並關閉執行時間測試。

skip

略過安裝,例如,在測試與 R 捆綁的建議套件時。

--no-install 相同:關閉安裝和需要安裝套件的測試。

下列環境變數可用於自訂 check 的操作:設定這些變數的便利位置是檢查環境檔案(預設為 ~/.R/check.Renviron)。

_R_CHECK_ALL_NON_ISO_C_

如果為 true,請勿忽略編譯器(通常為 GCC)關於 系統 標頭中非 ISO C 程式碼的警告。請注意,這也可能會顯示其他 ISO C++ 警告。預設值:false。

_R_CHECK_FORCE_SUGGESTS_

如果為 true,則在建議的套件不可用時傳回錯誤。預設值:true(但 CRAN 提交檢查為 false)。

_R_CHECK_RD_CONTENTS_

如果為 true,則檢查 Rd 檔案是否有需要編輯的自動產生內容,以及缺少的引數文件。預設值:true。

_R_CHECK_RD_LINE_WIDTHS_

如果為 true,則檢查 Rd 在使用和範例區段中的行寬。預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_RD_STYLE_

如果為 true,則檢查 Rd 使用 S3 方法的條目是否使用完整函式名稱,而不是適當的 \method 標記。預設值:true。

_R_CHECK_RD_XREFS_

如果為 true,則檢查 .Rd 檔案中的交叉參照。預設值:true。

_R_CHECK_SUBDIRS_NOCASE_

如果為 true,則檢查目錄(例如 Rman)的大小寫。預設值:true。

_R_CHECK_SUBDIRS_STRICT_

針對 --check-subdirs 的初始設定。預設值:‘default’(僅檢查 tarball,且僅在沒有 configure 檔案時檢查 src)。

_R_CHECK_USE_CODETOOLS_

若為 true,使用 codetools 套件,該套件提供物件可見性的詳細分析(但可能會產生誤報)。預設值:true(如果已安裝建議套件)。

_R_CHECK_USE_INSTALL_LOG_

若為 true,將套件安裝輸出的記錄作為檢查的一部分記錄到日誌檔(預設為 00install.out),即使在互動式執行時也是如此。預設值:true。

_R_CHECK_VIGNETTES_NLINES_

報告執行或重新建置小插圖時錯誤的輸出底部顯示的最大行數。(值 0 表示將顯示所有行。)預設值:執行為 10,重新建置為 25。

_R_CHECK_CODOC_S4_METHODS_

控制是否對 S4 方法執行 codoc() 測試。預設值:true。

_R_CHECK_DOT_INTERNAL_

控制是否掃描套件程式碼以尋找 .Internal 呼叫,這些呼叫僅應由基本套件(偶爾由建議套件)使用。預設值:true。

_R_CHECK_EXECUTABLES_

控制檢查可執行(二進位)檔案。預設值:true。

_R_CHECK_EXECUTABLES_EXCLUSIONS_

控制是否忽略封裝中的 BinaryFiles 檔案中所列的檔案,以檢查可執行(二進位)檔案。預設值:true(但 CRAN 提交檢查的預設值為 false)。不過,這個封裝層級的覆寫機制很可能會在未來被移除。

_R_CHECK_PERMISSIONS_

控制是否檢查檔案的權限。預設值:true,當且僅當 .Platform$OS.type == "unix"

_R_CHECK_FF_CALLS_

允許關閉 checkFF() 測試。如果設為「registration」,則檢查封裝已安裝時此類呼叫的註冊資訊(參數數量、正確選擇 .C/.Fortran/.Call/.External)。預設值:true。

_R_CHECK_FF_DUP_

控制 checkFF(check_DUP) 預設值:true(且強制 CRAN 提交檢查為 true)。

_R_CHECK_LICENSE_

控制是否/如何執行授權檢查。可能的值為「maybe」(如有問題則發出警告,但不會針對可標準化的非標準授權規範發出警告)。預設值:true。

_R_CHECK_RD_EXAMPLES_T_AND_F_

控制 check_T_and_F() 是否也會在範例中尋找「不良」(全域)「T」/「F」使用。預設為關閉,因為這可能會導致誤報。

_R_CHECK_RD_CHECKRD_MINLEVEL_

控制來自 checkRd 的警告報告的最低層級。預設值:-1。

_R_CHECK_RD_ALLOW_EMPTY_ITEM_IN_DESCRIBE_

控制 \\describe 內部是否允許空項目而不發出警告。這是一種臨時的權宜措施,旨在支援舊有套件,並可能在 R 的未來版本中移除。預設值:false。

_R_CHECK_XREFS_REPOSITORIES_

如果設定為非空值,則為一空白分隔的儲存庫清單,用於判斷已知的套件。預設值:空值,此時會使用 R 已知的 CRAN 和 Bioconductor 儲存庫。

_R_CHECK_SRC_MINUS_W_IMPLICIT_

控制是否檢查安裝輸出中關於隱式函式宣告的編譯警告(由 GCC 使用命令列選項 -Wimplicit-function-declaration 偵測到,而 -Wall 暗示了這一點)。注意:在某些最近的 C 編譯器中,隱式函式宣告是錯誤,包括 Apple clang。預設值:R 4.2.0 起為 true,先前為 false。

_R_CHECK_SRC_MINUS_W_UNUSED_

控制是否檢查安裝輸出中關於未使用的程式碼組成部分的編譯警告(由 GCC 使用命令列選項 -Wunused 偵測到,而 -Wall 暗示了這一點)。預設值:true。

_R_CHECK_WALL_FORTRAN_

控制是否在安裝輸出的分析中使用 gfortran 4.0 或更新版本的 -Wall 警告。預設值:false,即使警告是合理的。

_R_CHECK_ASCII_CODE_

如果為 true,檢查 R 程式碼中是否有非 ASCII 字元。預設值:true。

_R_CHECK_ASCII_DATA_

如果為 true,檢查資料中是否有非 ASCII 字元。在執行過程中,檢查所有資料集是否可以載入,以及其元件是否可以存取。預設值:true。

_R_CHECK_COMPACT_DATA_

如果為 true,檢查資料是否為 ASCII 和未壓縮儲存,並檢查使用 bzip2xz 壓縮是否會顯著改善。預設值:true。

_R_CHECK_SKIP_ARCH_

在多重架構設定中,將省略檢查的架構清單(以逗號分隔)。預設值:無。

_R_CHECK_SKIP_TESTS_ARCH_

在多重架構設定中,將省略執行測試的架構清單(以逗號分隔)。預設值:無。

_R_CHECK_SKIP_EXAMPLES_ARCH_

在多架構設定中,將省略執行範例的架構清單(以逗號分隔)。預設值:無。

_R_CHECK_VC_DIRS_

是否檢查解壓縮的套件目錄是否有版本控制目錄(CVS.svn …)?預設值:對於 tarball 為 true。

_R_CHECK_PKG_SIZES_

是否使用 du 來尋找已安裝套件的大小?R CMD check 會檢查 du 的可用性。但如果找到不適當的指令(包括不尊重 -k 旗標以 1Kb 為單位報告,或以不同格式報告的指令),這個選項允許覆寫檢查(GNU、macOS 和 Solaris du 指令已過測試)。預設值:如果找到 du,則為 true。

_R_CHECK_PKG_SIZES_THRESHOLD_

用於 _R_CHECK_PKG_SIZES_ 的閾值(以 Mb 為單位)。預設值:5

_R_CHECK_DOC_SIZES_

是否使用 qpdf 來檢查已安裝 PDF 的大小?預設值:如果找到 qpdf,則為 true。

_R_CHECK_DOC_SIZES2_

是否應使用 gs 檢查 PDF 已安裝大小?這比(並額外)先前的檢查慢,但會偵測過多細節(通常會被過度繪製隱藏)或解析度過高的位圖。需要將 R_GSCMD 設定為有效的程式,或 gs(或在 Windows 上,gswin32.exegswin64c.exe)在路徑中。預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_ALWAYS_LOG_VIGNETTE_OUTPUT_

預設值僅在有錯誤時,才會保留執行範例中 R 程式碼的輸出。這也適用於重新建置範例的 build_vignettes.log 記錄檔。預設值:false。

_R_CHECK_CLEAN_VIGN_TEST_

如果測試成功,是否應移除 vign_test 目錄?預設值:true。

_R_CHECK_REPLACING_IMPORTS_

是否應回報關於取代匯入的警告?這些有時來自其他套件中自動產生的 NAMESPACE 檔案,但最常來自匯入整個名稱空間,而不是使用 importFrom。預設值:true。

_R_CHECK_UNSAFE_CALLS_

檢查看似竄改(或允許竄改)已載入的非來自目前套件程式碼的呼叫:此類呼叫很可能違反 CRAN 政策。預設值:true。

_R_CHECK_TIMINGS_

選擇性地報告安裝、範例、測試和執行/重新建置範例的時機,作為檢查記錄的一部分。格式為「[as/bs]」,代表總 CPU 時間(包括子程序)「a」和經過時間「b」,不過在 Windows 上,格式為「[bs]」。在多數情況下,僅提供「OK」檢查的時機。經過時間超過 10 分鐘的時機以分鐘為單位報告(縮寫為「m」)。值為應報告的經過時間秒數中的最小數值值:非數值值表示不需要報告,值「0」表示總是需要報告。預設值:""。 (除非在環境中設定,否則 CRAN 檢查為「10」。)

_R_CHECK_EXAMPLE_TIMING_THRESHOLD_

如果正在記錄時機,請設定報告執行時間過長的範例的秒數閾值(使用者 + 系統 CPU 時間或經過時間)。預設值:"5"

_R_CHECK_EXAMPLE_TIMING_CPU_TO_ELAPSED_THRESHOLD_

對於已啟用時機的檢查,報告 CPU 時間與經過時間的比率超過此閾值(且 CPU 時間至少一秒)的範例。這有助於偵測同時使用多個 CPU 核心。預設值:NA

_R_CHECK_TEST_TIMING_CPU_TO_ELAPSED_THRESHOLD_

如果 CPU 時間與經過時間的比率超過此臨界值(且 CPU 時間至少為一秒),則執行個別測試的報告。Windows 不支援。預設值:NA

_R_CHECK_VIGNETTE_TIMING_CPU_TO_ELAPSED_THRESHOLD_

如果執行/重新建置範例(個別或總計)時,CPU 時間與經過時間的比率超過此臨界值(且 CPU 時間至少為一秒),則執行報告。Windows 不支援。預設值:NA

_R_CHECK_CODETOOLS_PROFILE_

字串,包含以逗號分隔的 名稱= 配對( 為邏輯常數),提供用於分析套件程式碼的 codetools 函式的其他引數。例如,使用 _R_CHECK_CODETOOLS_PROFILE_="suppressLocalUnused=FALSE" 來關閉關於未使用的局部變數的警告。預設值:沒有其他引數,對應於使用 skipWith = TRUEsuppressPartialMatchArgs = FALSEsuppressLocalUnused = TRUE

_R_CHECK_CRAN_INCOMING_

檢查套件是否適合在 CRAN 上發布。預設值:false,但 CRAN 提交檢查除外。

_R_CHECK_CRAN_INCOMING_REMOTE_

包含上述需要遠端存取的檢查。預設值:與 _R_CHECK_CRAN_INCOMING_ 相同

_R_CHECK_XREFS_USE_ALIASES_FROM_CRAN_

檢查錨定的 Rd 交叉參照時,除了使用本機安裝套件中的別名外,還使用 CRAN 套件 Web 區域中的 Rd 別名。預設值:false。

_R_SHLIB_BUILD_OBJECTS_SYMBOL_TABLES_

在安裝時,透過記錄物件 (.o 檔案) 的符號表在 symbols.rds 檔案中,讓編譯程式碼的檢查更準確。(目前僅支援 Linux、Solaris、macOS、Windows 和 FreeBSD。)預設值:true。

_R_CHECK_CODE_ASSIGN_TO_GLOBALENV_

是否應檢查套件程式碼是否指派給全域環境?預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_CODE_ATTACH_

是否應檢查套件程式碼是否有呼叫 attach()?預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_CODE_DATA_INTO_GLOBALENV_

是否應檢查套件程式碼是否有呼叫會載入到全域環境的 data()?預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_DOT_FIRSTLIB_

是否應檢查套件程式碼是否存在已過時的函數 .First.lib()?預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_DEPRECATED_DEFUNCT_

套件程式碼是否應檢查最近已棄用或停用的函數(包括已完全移除的函數)。也適用於特定於平台的圖形裝置。預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_SCREEN_DEVICE_

如果設定為「warn」,當範例等開啟螢幕裝置時,會發出警告。如果設定為「stop」,會發出錯誤。預設值:空值(但 CRAN 提交檢查為「stop」)。

_R_CHECK_WINDOWS_DEVICE_

如果設定為「stop」,當範例等中使用僅限 Windows 的裝置時,會發出錯誤。這僅適用於 Windows:裝置在其他地方不存在。預設值:空值(但 Windows 上的 CRAN 提交檢查為「stop」)。

_R_CHECK_TOPLEVEL_FILES_

報告套件來源中未在「撰寫 R 擴充套件」中描述,也未被普遍理解(例如 ChangeLog)的頂層檔案。標準名稱的變體(例如 COPYRIGHT)也會被報告。預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_GCT_N_

--use-gct 是否應使用 gctorture2(n) 而不是 gctorture(TRUE)?使用正整數來啟用此功能。預設值:0

_R_CHECK_LIMIT_CORES_

如果設定,檢查套件 parallel 中過多核心的使用。如果設定為「warn」,會發出警告;如果設定為「false」或「FALSE」,會略過檢查;任何其他非空值會在產生超過 2 個子項時發出錯誤。預設值:未設定(但 CRAN 提交檢查為「TRUE」)。

_R_CHECK_CODE_USAGE_VIA_NAMESPACES_

如果設定,直接在套件名稱空間上檢查程式碼使用情況(透過 codetools),而不會載入和附加套件及其建議和增強功能。預設值:true(且為 CRAN 提交檢查的 true)。

_R_CHECK_CODE_USAGE_WITH_ONLY_BASE_ATTACHED_

如果設定,只附加基本套件來檢查程式碼使用情況(透過 codetools)。預設值:true。

_R_CHECK_EXIT_ON_FIRST_ERROR_

如果設定為 true 值,檢查會在第一個錯誤時結束。預設值:false。

_R_CHECK_OVERWRITE_REGISTERED_S3_METHODS_

如果設定為 true 值,回報在載入此套件的名稱空間時,會覆寫基本/建議套件中已註冊的 S3 方法。預設值:false(但為 CRAN 提交檢查的 true)。

_R_CHECK_TESTS_NLINES_

在記錄檔中重製的測試輸出的尾行數。如果 0,則重製 R 前言以外的所有行。預設值:13。

_R_CHECK_NATIVE_ROUTINE_REGISTRATION_

如果設定為真值,則報告在套件的 DLL 中找不到註冊原生常式的進入點和抑制動態搜尋。(注意:這需要系統指令 nmPATH 中。在 Windows 上,會先在透過 Makeconf 指定的編譯器工具鏈中搜尋 objdump.exe(可透過環境變數 BINPREF 自訂)。如果在那裡找不到,則必須在 PATH 中。在 Unix 上,這在使用已編譯程式碼的套件時會是正常的(這是唯一會檢查的),但 Windows 使用者應該檢查。預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_NO_STOP_ON_TEST_ERROR_

如果設定為真值,則在第一次錯誤後不要停止執行測試(就像給定命令列選項 --no-stop-on-test-error 一樣)。預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_PRAGMAS_

對 C/C++ 原始碼和標頭中的語法執行其他檢查。預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_COMPILATION_FLAGS_

如果套件已安裝且有 C/C++/Fortran 程式碼,請檢查安裝記錄檔中是否有不可移植的旗標(例如在組態期間新增到 src/Makevars 的旗標)。目前會報告 -W 旗標,但 -Wall-Wextra-Weverything 除外,以及看起來像是嘗試抑制警告的旗標會被標示出來。請參閱 撰寫 R 擴充套件 中的 撰寫可移植套件,了解此檢查的原理(以及為什麼連 -Werror 都不安全)。

環境變數 _R_CHECK_COMPILATION_FLAGS_KNOWN_ 可以設定為一個空白分隔的旗標組,這些旗標來自用於測試的 R 建置(例如 -Wall-Wextra 旗標已知)。例如,對於 macOS 上 R >= 4.0.0 的 CRAN 建置,可以使用

_R_CHECK_COMPILATION_FLAGS_KNOWN_="-mmacosx-version-min=10.13" 

預設值:未設定。

_R_CHECK_R_DEPENDS_

檢查對 R 的任何依賴性是否不是最近的修補程式層級版本,例如 R (>= 3.3.3),因為封鎖套件的安裝也會封鎖其反向依賴性。可能的數值為「"note"」、「"warn"」和邏輯值(其中目前 true 值等於「"note"」)。預設值:false(但對於 --as-cran 為「"warn"」)。

_R_CHECK_SERIALIZATION_

檢查套件來源中的序列化 R 物件是否已使用版本 2 序列化,且沒有依賴於「R >= 3.5.0」。(版本 3 自 R 3.5.0 起使用,但僅應在必要時使用。)預設值:false(但對於 CRAN 提交檢查為 true)。

_R_CHECK_R_ON_PATH_

這會檢查套件是否嘗試從路徑而非測試中的路徑使用 RRscript。它會透過將指令碼置於路徑開頭來執行此動作,這些指令碼會印出訊息並失敗。預設值:false(但對於 CRAN 提交檢查為 true)。

_R_CHECK_PACKAGES_USED_IN_TESTS_USE_SUBDIRS_

如果設為真值,也會檢查 tests 中常見單元測試子目錄中的 R 程式碼,是否有未宣告的套件依賴項。預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_SHLIB_OPENMP_FLAGS_

檢查 src/Makevars(及類似檔案)中 SHLIB_OPENMP_*FLAGS 的正確且可攜式使用。預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_CONNECTIONS_LEFT_OPEN_

檢查範例時,檢查每個範例的連線是否未關閉:如果發現任何未關閉的連線,會以致命錯誤回報。注意:「連線」包括大多數檔案使用,以及任何尚未由 stopCluster() 停止的平行叢集。預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_FUTURE_FILE_TIMESTAMPS_

檢查任何輸入檔案的時間戳記是否在未來(且為此檢查系統時鐘是否正確,誤差在 5 分鐘內)。預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_LENGTH_1_CONDITION_

不再使用:長度大於 1 的條件在 ifwhile 陳述式中現在會產生錯誤。

_R_CHECK_VIGNETTES_SKIP_RUN_MAYBE_

如果要重建小插圖輸出(這會涉及執行該程式碼),是否應該略過在小插圖中執行 R 程式碼。預設值:false(但對於 CRAN 檢查為 true)

_R_CHECK_BUILD_VIGNETTES_SEPARATELY_

在 R 3.6.0 之前,重新建置小插圖輸出是在單一 R 會話中完成的,這允許一個小插圖意外依賴另一個小插圖(例如,在載入套件時)。目前的預設值是為每個小插圖使用一個獨立的會話;此選項允許測試較舊的行為,預設值:true

_R_CHECK_SYSTEM_CLOCK_

作為 --as-cran 啟用的「檢查未來檔案時間戳記」的一部分,根據外部時鐘檢查系統時鐘,以捕捉錯誤,例如錯誤的日期甚至年份。在執行重複檢查的系統上並非必要。預設值:true(但對於 CRAN 檢查為 false)

_R_CHECK_AUTOCONF_

對於具有由 GNU autoconfconfigure.acconfigure,.in 產生的 configure 檔案的套件,請檢查是否可以在來源的副本中執行 autoreconf(如果可用)(這將偵測遺失的來源檔案並報告 autoconf 警告)。環境變數 AUTORECONf 控制所使用的指令:它可以提供 autoreconf 的完整路徑(沒有空格),並且可以包含旗標,例如 --warnings=obsolete(已新增至 autoreconf 版本 2.68 或 2.69,並且是後續版本的預設值)。預設值:false(但對於 CRAN 送出檢查為 true)。

_R_CHECK_DATALIST_

檢查檔案 data/datalist 是否過期。預設值:false(但對於 CRAN 送出檢查為 true)。

_R_CHECK_THINGS_IN_CHECK_DIR_

在檢查執行結束時檢查並報告是否已將檔案留在檢查目錄中。預設值:false(但對於 CRAN 送出檢查為 true)。

_R_CHECK_THINGS_IN_TEMP_DIR_

在檢查執行結束時檢查並回報檔案是否會遺留在暫時目錄中(通常在類 Unix 系統上為 /tmp)。它會將環境變數 TEMPDIR 設定為 check 程序的 R 會話目錄的子目錄:如果任何檔案或目錄遺留在其中,它們會被移除。由於其中一些檔案或目錄可能超出使用者的控制範圍,環境變數 _R_CHECK_THINGS_IN_TEMP_DIR_EXCLUDE_ 可以指定一個不應回報的檔案路徑(延伸正規表示式)樣式,CRAN 使用「^ompi.」來表示 OpenMPI 遺留的目錄。有少數情況 TEMPDIR 無法被遵循,因此檔案會遺留在 /tmp 中(而且不會回報,但請參閱下一項):一個範例是在某些作業系統上的 /tmp/boost_interprocess。預設值:false(但對於 CRAN 提交檢查為 true)。

_R_CHECK_THINGS_IN_OTHER_DIRS_

在檢查執行結束時檢查並回報在檢查執行期間是否在選定的目錄集中建立新的檔案或目錄。(這僅限於執行檢查程序的使用者擁有的檔案。)目前監控的目錄為家目錄、/tmp(排除 RtmpXXXXXX 目錄)、/dav/shm~/.cache(遞迴)和 ~/.local/share(遞迴)或其在 Windows 和 macOS 上的等效目錄(tools::R_user_dir() 的預設設定使用的目錄在 R 子目錄中)。其他目錄可以指定在環境變數 _R_CHECK_THINGS_IN_OTHER_DIRS_XTRA_ 中,使用分號分隔。目錄會在所有平台上以尾隨「/」回報。

環境變數 _R_CHECK_THINGS_IN_OTHER_DIRS_EXCLUDE_ 可以指定一個(延伸正規表示法)檔案路徑的模式,不予回報 – 這應該符合絕對檔案路徑,其中主目錄以 ~ 表示。例如,在 Linux 系統上

'^~/.cache/(mozilla/firefox|mesa_shader_cache)/'

符合 Firefox 和 OpenGL(及其內容)使用的快取目錄。如果值以「@」開頭,則視為檔案路徑,其中每一行視為要符合的模式。

請注意,其他程序(包括並行檢查執行)可能會在這些目錄中建立新檔案,這些檔案將會回報。然而,這個選擇性檢查對於縮小可能留下意外檔案的套件非常有用。預設值:false

_R_CHECK_BASHISMS_

使用 Perl 腳本 checkbashisms(如果可用)檢查頂層腳本 configure(除非由 autoconf 產生)和 cleanup,以找出非 Bourne shell 程式碼,其中包括回報使用不可移植的 #! /bin/bash 的腳本。(腳本 checkbashisms 在大多數 Linux 發行版中可用,套件名稱為「devscripts」或「devscripts-checkbashisms」,也可以從 https://sourceforge.net/projects/checkbaskisms/files 取得。)預設值:false(但對於 CRAN 提交檢查為 true,Windows 除外)。

_R_CHECK_ORPHANED_

檢查相依性是否為孤立套件。從 R 4.1.0 開始,這會遞迴檢查嚴格相依性,因此會回報任何孤立套件,這些套件是透過 library() 附加套件所需要的,以及任何建議的孤立套件。預設值:false(但對於 CRAN 提交檢查為 true)。

_R_CHECK_EXCESSIVE_IMPORTS_

正整數。如果設定,當從非基本套件匯入的數量超過此閾值時,會提供備註。大量匯入會使套件容易受到其中任何一個無法使用時所造成的影響。預設:未設定(但 CRAN 提交檢查為 20)

_R_CHECK_DONTTEST_EXAMPLES_

如果為 true,且範例中找到有 \donttest 區段,則會在一次執行中執行測試,並將這些區段註解掉,然後在第二次執行中包含 \donttest 區段(僅限於主要架構)。只有在第一次執行時,才會將結果與任何 .Rout.save 檔案進行比較,並分析執行時間。會被 --run-donttest 覆寫。預設:false,除非指定 --as-cran(可透過設定「_R_CHECK_DONTTEST_EXAMPLES_=false」來覆寫)。

_R_CHECK_XREFS_PKGS_ARE_DECLARED_

檢查 .Rd 檔案中「錨定」交叉參考中所使用的套件(格式為 \link[pkg]{foo}\link[pkg:bar]{foo})是否已在 DESCRIPTION 檔案中宣告,以便檢查這些連結。預設:false。

_R_CHECK_XREFS_MIND_SUSPECT_ANCHORS_

檢查套件錨定的 Rd 交叉參考是否為 檔案(而非別名)。預設:false。

_R_CHECK_BOGUS_RETURN_

如果為 true,且 _R_CHECK_USE_CODETOOLS_ 也為 true,則會掃描函式,以檢查是否使用 return 而不是 return()。預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_MATRIX_DATA_

預設情況下,檢查 matrix 呼叫中資料長度與維度不符時,會發出警告:將此設定為 true 值會產生錯誤,並附上簡潔的追蹤。預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_CODE_CLASS_IS_STRING_

檢查套件程式碼中是否有 if() 條件,用於將物件的類別與字串進行比較。請參閱 https://blog.r-project.org/2019/11/09/when-you-think-class.-think-again/,了解為何這是一個壞主意。預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_RD_VALIDATE_RD2HTML_

使用 HTML Tidy (https://www.html-tidy.org/)檢查套件 HTML 說明頁面的有效性(如果可執行檔系統路徑中提供適當版本,或環境變數 R_TIDYCMD 指定其路徑)。建議 macOS 使用者從 https://binaries.html-tidy.org/ 安裝 5.8.0 或更新版本。預設值:false(但 CRAN 提交檢查為 true)。--as-cran 可以透過將此變數設定為 false 值來覆寫。

_R_CHECK_RD_MATH_RENDERING_

檢查透過 KaTeX 進行的 HTML 數學運算是否運作(如果套件 V8 可用)。請參閱 https://blog.r-project.org/2022/04/08/enhancements-to-html-documentation/。預設值:false(但 CRAN 提交檢查為 true)。

_R_CHECK_NEWS_IN_PLAIN_TEXT_

檢查 news() 是否能成功讀取純文字 (檔案 NEWS) 格式的套件新聞。預設值:false (但 CRAN 提交檢查為 true)。

_R_WIN_CHECK_INVALID_PARAMETERS_

檢查傳遞給 Windows C 執行時期的引數是否有效。啟用時,如果以無效引數呼叫 C 執行時期函式,R 會列印診斷資訊並無條件終止。預設情況下,遵循 MinGW-W64 預設值的 Windows 版 R 會導致 UCRT 忽略無效引數,但當 R 嵌入到使用不同編譯器工具鏈建置的應用程式中時,它們可能會導致 R 中止。可以將值設定為 "watson" 以透過 _invoke_watson 在 Windows 上進行互動式除錯。預設值:停用。

_R_CHECK_BROWSER_NONINTERACTIVE_

如果設定為 true 值,則會攔截非互動式除錯器呼叫。這些呼叫最可能是套件程式碼中殘留的 browser() 陳述式所導致。預設值:未設定 (但 CRAN 提交檢查為 true)。

下列變數控制檢查其他套件的未宣告/無條件使用。它們透過設定暫時函式庫目錄,並將 .libPaths() 設定為僅該目錄和 .Library,因此僅在其他套件安裝於 .Library 以外的位置時才會生效。暫時函式庫會填入已安裝套件的符號連結22,這些套件不在 .Library 中。

_R_CHECK_INSTALL_DEPENDS_

如果設定為真值,且要進行測試安裝,此動作會使用所有 Depends/Imports/LinkingTo 套件填入的暫時函式庫來執行。預設值:false(但 CRAN 提交檢查為 true)。

請注意,這實際上是在 R CMD INSTALL 中實作,因此對那些先將記錄安裝到日誌,然後呼叫 R CMD check 的人可用。

_R_CHECK_SUGGESTS_ONLY_

如果設定為真值,執行範例、測試和範例會使用所有 Depends/Imports/Suggests 套件填入的暫時函式庫目錄來執行。(作為例外,‘VignetteBuilder’ 欄位中的套件始終可用。)預設值:false(但 CRAN 提交檢查為 true:一些常規檢查使用 true,而另一些使用 false)。

_R_CHECK_DEPENDS_ONLY_

_R_CHECK_SUGGESTS_ONLY_ 相同,但僅使用 Depends/Imports(以及例外,包括 ‘Suggests’ 中的測試套件管理員)。預設值:false

_R_CHECK_DEPENDS_ONLY_DATA_

僅將 _R_CHECK_DEPENDS_ONLY_ 套用到從 data 目錄載入的檢查,因此檢查是否有任何資料集依賴於 Suggests 或未宣告的套件。預設值:false(但 CRAN 提交檢查為 true)

_R_CHECK_DEPENDS_005fONLY_005fEXAMPLES_
_R_CHECK_DEPENDS_005fONLY_005fTESTS_
_R_CHECK_DEPENDS_005fONLY_005fVIGNETTES_

僅將 _R_CHECK_DEPENDS_ONLY_ 套用於範例、測試或小品文的檢查。這些可以單獨使用,或使用 false 值來覆寫 _R_CHECK_DEPENDS_ONLY_。預設值:_R_CHECK_DEPENDS_ONLY_ 的值,或在未設定時為 false。

_R_CHECK_005fNO_005fRECOMMENDED_

如果設定為 true 值,則擴充先前的檢查,讓建議的套件在未宣告時無法使用(即使已安裝在 .Library 中)。預設值:false(但對於 CRAN 提交檢查為 true)。

這可能會對使用 grDevices::densColsstats:::.asSparse / stats:::.Diag 的程式碼產生誤報,因為它們分別呼叫 KernSmoothMatrix。(如果使用 sparse = TRUE,則 stats 中的那些會從各種對比函式中呼叫。)

CRAN 的提交檢查使用類似

_R_CHECK_CRAN_INCOMING_=TRUE
_R_CHECK_CRAN_INCOMING_REMOTE_=TRUE
_R_CHECK_VC_DIRS_=TRUE
_R_CHECK_TIMINGS_=10
_R_CHECK_INSTALL_DEPENDS_=TRUE
_R_CHECK_SUGGESTS_ONLY_=TRUE
_R_CHECK_NO_RECOMMENDED_=TRUE
_R_CHECK_EXECUTABLES_EXCLUSIONS_=FALSE
_R_CHECK_DOC_SIZES2_=TRUE
_R_CHECK_CODE_ASSIGN_TO_GLOBALENV_=TRUE
_R_CHECK_CODE_ATTACH_=TRUE
_R_CHECK_CODE_DATA_INTO_GLOBALENV_=TRUE
_R_CHECK_CODE_USAGE_VIA_NAMESPACES_=TRUE
_R_CHECK_DOT_FIRSTLIB_=TRUE
_R_CHECK_DEPRECATED_DEFUNCT_=TRUE
_R_CHECK_REPLACING_IMPORTS_=TRUE
_R_CHECK_SCREEN_DEVICE_=stop
_R_CHECK_TOPLEVEL_FILES_=TRUE
_R_CHECK_OVERWRITE_REGISTERED_S3_METHODS_=TRUE
_R_CHECK_PRAGMAS_=TRUE
_R_CHECK_COMPILATION_FLAGS_=TRUE
_R_CHECK_R_DEPENDS_=warn
_R_CHECK_SERIALIZATION_=TRUE
_R_CHECK_R_ON_PATH_=TRUE
_R_CHECK_PACKAGES_USED_IN_TESTS_USE_SUBDIRS_=TRUE
_R_CHECK_SHLIB_OPENMP_FLAGS_=TRUE
_R_CHECK_CONNECTIONS_LEFT_OPEN_=TRUE
_R_CHECK_FUTURE_FILE_TIMESTAMPS_=TRUE
_R_CHECK_AUTOCONF_=true
_R_CHECK_DATALIST_=true
_R_CHECK_THINGS_IN_CHECK_DIR_=true
_R_CHECK_THINGS_IN_TEMP_DIR_=true
_R_CHECK_BASHISMS_=true
_R_CHECK_ORPHANED_=true
_R_CHECK_BOGUS_RETURN_=true
_R_CHECK_MATRIX_DATA_=TRUE
_R_CHECK_CODE_CLASS_IS_STRING_=true
_R_CHECK_RD_VALIDATE_RD2HTML_=true
_R_CHECK_RD_MATH_RENDERING_=true
_R_CHECK_NEWS_IN_PLAIN_TEXT_=true
_R_CHECK_BROWSER_NONINTERACTIVE_=true

這些是由 R CMD check --as-cran 開啟的:傳入的檢查也使用

_R_CHECK_FORCE_SUGGESTS_=FALSE

因為有些套件會建議在 CRAN 或其他常用存放庫中無法取得的其他套件。

可以利用多個環境變數來設定「逾時」:用於檢查部分子程序所花費的時間限制。值 0 表示沒有限制,且為預設值。字串結尾為「s」、「m」或「h」表示秒、分或小時:其他值會被解釋為整數秒(無效輸入會被視為沒有限制)。

_R_CHECK_ELAPSED_TIMEOUT_

未另行提及的子程序的預設逾時,以及 _R_CHECK_ONE_TEST_ELAPSED_TIMEOUT_ 以外所有逾時的預設值。(tools::check_packages_in_dir 也會使用此值。)

_R_CHECK_INSTALL_ELAPSED_TIMEOUT_

check 執行 R CMD INSTALL 時的限制。

_R_CHECK_EXAMPLES_ELAPSED_TIMEOUT_

執行一個子架構的所有範例的限制。

_R_CHECK_ONE_TEST_ELAPSED_TIMEOUT_

執行一個子架構的一個測試的限制。預設值為 _R_CHECK_TESTS_ELAPSED_TIMEOUT_

_R_CHECK_TESTS_ELAPSED_TIMEOUT_

執行所有子架構測試的限制(以及執行單一測試的預設限制)。

_R_CHECK_ONE_VIGNETTE_ELAPSED_TIMEOUT_

執行單一範例中的 R 程式碼的限制,包括個別重新建置每個範例。

_R_CHECK_BUILD_VIGNETTES_ELAPSED_TIMEOUT_

重新建置所有範例的限制。

_R_CHECK_PKGMAN_ELAPSED_TIMEOUT_

嘗試建置 PDF 套件手冊的每個限制。

另一個可啟用更嚴格檢查的變數是將 R_CHECK_CONSTANTS 設定為 5。這會檢查沒有任何變數23變更 R 程式碼中「常數」24 的值。這最適合與將 R_JIT_STRATEGY 設定為 3 搭配使用,這會在第一次使用時檢查程式碼(預設情況下,大部分程式碼只會在第二次使用時,在位元組編譯後檢查)。不幸的是,這些檢查會減慢範例、測試和範例的檢查速度,通常會減慢兩倍,但在最糟的情況下,至少會減慢一百倍。

下列環境變數可用於自訂 INSTALL 的操作。

_R_INSTALL_LIBS_ONLY_FORCE_DEPENDS_IMPORTS_

如果為 true,則在透過 --libs-only 安裝套件函式庫時,如果某些套件匯入或依賴的套件不可用,則會傳回錯誤。預設值:true(僅對分析套件原生程式碼的特殊應用程式為 false)。


9 R 編碼標準

R 旨在於各種平台上執行,包括 Linux 和大多數 Unix 變體,以及 Windows 和 macOS。因此,在透過新增至 R 基礎發行版或提供附加套件來擴充 R 時,如果可以避免,不應依賴僅限於少數受支援平台的特點。特別是,儘管大多數 R 開發人員使用 GNU 工具,但他們不應使用標準工具的 GNU 擴充功能。雖然其他一些軟體套件明確依賴於例如 GNU make 或 GNU C++ 編譯器,但 R 並不依賴。儘管如此,R 仍是一個 GNU 專案,並且如果可能的話,應遵循 GNU 編碼標準 的精神。

以下工具可「安全地假設」適用於 R 擴充功能。

在 Windows 下,大多數使用者不會安裝這些工具,您不應要求您的套件運作時必須要有這些工具。但是,從來源安裝套件的使用者會有這些工具,因為可以假設他們已遵循「R 安裝與管理」手冊的「Windows 工具組」附錄中的說明取得這些工具。無法假設可透過 system 取得重新導向,因為這不會使用標準 shell(更不用說 Bourne shell)。

此外,特定工作需要下列工具。

讓其他人也能理解程式碼撰寫方式也很重要。這對於修正問題特別有幫助,包括使用有意義的變數名稱、註解程式碼,以及適當地格式化程式碼。R 核心團隊建議 R 和 C(以及很可能還有 Perl)程式碼使用基本縮排 4,Rd 格式的文件則使用 2。Emacs(21 或更新版本)使用者可以將以下內容放入其中一個啟動檔案中,並使用自訂功能將 c-default-style 設定為 "bsd",將 c-basic-offset 設定為 4,來實作這種縮排樣式。

;;; ESS
(add-hook 'ess-mode-hook
          (lambda ()
            (ess-set-style 'C++ 'quiet)
            ;; Because
            ;;                                 DEF GNU BSD K&R C++
            ;; ess-indent-level                  2   2   8   5   4
            ;; ess-continued-statement-offset    2   2   8   5   4
            ;; ess-brace-offset                  0   0  -8  -5  -4
            ;; ess-arg-function-offset           2   4   0   0   0
            ;; ess-expression-offset             4   2   8   5   4
            ;; ess-else-offset                   0   0   0   0   0
            ;; ess-close-brace-offset            0   0   0   0   0
            (add-hook 'local-write-file-hooks
                      (lambda ()
                        (ess-nuke-trailing-whitespace)))))
(setq ess-nuke-trailing-whitespace-p 'ask)
;; or even
;; (setq ess-nuke-trailing-whitespace-p t)
;;; Perl
(add-hook 'perl-mode-hook
          (lambda () (setq perl-indent-level 4)))

(Emacs 的 C 和 R 模式的「GNU」樣式使用基本縮排 2,已確定在使用窄字型時,這種縮排不足以清楚顯示結構。)


10 測試 R 程式碼

當您(作為 R 開發人員)將新函式新增到 R 基底(所有與 R 一起發行的套件)時,請務必檢查 make test-Specific 或特別是 cd tests; make no-segfault.Rout 是否仍然有效(無需使用者互動,且在獨立電腦上)。例如,如果新函式會存取網際網路,或需要 GUI 互動,請將其名稱新增到 tests/no-segfault.Rin 中的「停止清單」。

[待修改:使用 make check-devel,如果您變更內部結構,請檢查寫入障礙。]


11 使用 TeX 方言

R 中使用各種 TeX 方言以達成不同的目的。政策是手冊以「texinfo」撰寫,為方便起見,主手冊和 Windows 常見問答集也是如此。這具有容易產生 HTML 和純文字版本以及排版手冊的優點。

LaTeX 不是直接使用,而是作為排版說明文件和範例的中間格式。

需要小心 R 使用者的系統所做的假設:它可能沒有安裝「texinfo」或 TeX 系統。我們已嘗試抽象出跨平台差異,且幾乎所有排版文件的設定都是由 tools::texi2dvi 完成。這用於說明文件的離線列印、準備範例和透過 R CMD Rd2pdf 的套件手冊。目前沒有用於目錄 doc/manual 中建立的 R 手冊。

tools::texi2dvi 使用系統指令 texi2dvi(如果可用)。在類 Unix 系統上,這通常是「texinfo」的一部分,而在 Windows 上,如果存在,它會是一個可執行檔,是 MiKTeX 的一部分。如果沒有可用,R 程式碼會執行一系列 (pdf)latexbibtexmakeindex 指令。

這個流程相當容易受到所使用的外部軟體版本影響:特別是 texi2dvitexinfo.tex 更新、兩者之間的不相容性25、LaTeX 套件「hyperref」的版本,以及索引製作中的怪癖。LaTeX 和後來的「texinfo」所使用的授權禁止我們在 R 的發行版中包含「已知良好的」版本。

在類 Unix 系統上,configure 會尋找 TeX 和相關程式的可執行檔,如果找到,就會在系統的 Renviron 檔案中記錄絕對路徑。以前如果找不到命令,會記錄「false」,但現在會記錄名稱,以便在執行時在路徑中查詢。後者對於二進位發行版很重要:例如,不希望被綁定在 TeX Live 2007 上。


12 目前和未來的方向

此章節是關於 R 可能正在進行和未來變更的備註:沒有承諾要發布這些變更,更不用說時間表了。


12.1 長向量

R 2.x.y 中的向量長度限制為 2^31 - 1 個元素(約 20 億),因為長度儲存在 SEXPREC 中,為 C int,而且此類型廣泛用於記錄長度和元素編號,包括在套件中。

請注意,在 32 位元平台上,由於其位址限制,更長的向量實際上是不可能的,因此本節僅適用於 64 位元平台。在 32 位元版本的 R 中,內部結構不變。

如果整數或邏輯型,則具有 2^31 或更多元素的單一物件將佔用至少 8GB 的記憶體;如果為數字或字元,則佔用 16GB,因此此類物件的例行使用仍有一段距離。

現在有一些支援長向量的功能。這適用於原始、邏輯、整數、數字和字元向量,以及清單和表達式向量。(字元向量(CHARSXP)的元素仍限制為 2^31 - 1 位元組。)一些考量

  • 這是透過將長度(和真實長度)記錄為 -1,並將實際長度記錄為標頭開頭的 64 位元欄位來實作的。由於 R 中相當大量的程式碼對長度使用有號類型,因此「長度」使用有號 C99 類型 ptrdiff_t 記錄,其 typedef 為 R_xlen_t
  • 理論上,這些可以有 63 位元的長度,但請注意,目前的 64 位元作業系統在理論上甚至不提供 64 位元位址空間,而且目前有 52 位元的限制(超過目前作業系統的理論限制,並確保此類長度可以準確地儲存在雙倍精度的數字中)。
  • 序列化格式已變更以容納更長的長度,但長度最長為 2^31-1 的向量儲存方式與之前相同。較長的向量會將其長度欄位設定為 -1,後面接著兩個 32 位元欄位,提供實際長度的上 32 位元和下 32 位元。目前有一個健全性檢查,會在反序列化時將長度限制為 2^48。
  • 類型 R_xlen_t 可在 C 標頭 Rinternals.h 中提供給套件:由於需要 C99,因此這在 C 程式碼中應該沒問題。人們確實會嘗試在 C++ 中使用 R 內部函數,但 C++98 編譯器不需要支援這些類型。
  • 編製索引可以使用雙精度數值。內部編製索引程式碼用於處理正整數索引(負數、邏輯和矩陣索引全部轉換為正整數):現在它可以處理 INTSXPREALSXP 索引。
  • 如果長度超過 2^31-1,R 函數 length 會傳回雙精度數值。在傳遞給 .C/.Fortran 之前呼叫 as.integer(length(x)) 的程式碼應該檢查 NA 結果。

12.2 64 位元類型

也有些人希望能在 R 中儲存較大的整數,儘管儲存為 double 的可能性常常被忽略(例如,檔案指標已由 seek 傳回並儲存為 double)。

已提出不同的途徑

  • 新增一個類型至 R 並將其用於長度和索引,最有可能的是 64 位元有號類型,例如 longint。R 的一般隱式強制轉換規則會確保提供 integer 向量用於索引或 length<- 會運作。
  • 更徹底的替代方案是將現有的 integer 類型變更為 64 位元平台上的 64 位元(這是 S-PLUS 對 DEC/Compaq Alpha 系統採取的方法)。甚至可以在所有平台上使用。
  • 允許 integerdouble 值用於長度和索引,並僅在必要時傳回 double

第三個方法的優點是對現有程式碼的干擾最小,且不會增加記憶體需求。在第一個和第三個場景中,R 自身的程式碼和使用者程式碼都必須調整為非 integer 類型的長度,而在第三個程式碼分支中,會很少測試長向量。

大多數 .C.Fortran 介面的使用者會使用 as.integer 作為長度和元素編號,但少數人會省略這些,因為他們知道這些是 integer 類型。假設這些永遠不會用於長向量可能是合理的。

其餘的介面需要應付已變更的 VECTOR_SEXPREC 類型。在多數情況下,長度似乎是由 lengthLENGTH 函數存取的26 目前的做法是讓這些函數傳回 32 位元長度,並引入傳回 R_xlen_t 值的「長」版本 xlengthXLENGTH

另請參閱 https://homepage.cs.uiowa.edu/~luke/talks/useR10.pdf


12.3 大型矩陣

矩陣儲存為向量,因此也限制在 2^31-1 個元素。現在在 64 位元平台上允許使用較長的向量,只要每個維度不超過 2^31-1,便支援具有更多元素的矩陣。但是,並非所有應用程式都能支援。

主要問題是由使用 32 位元 INTEGER 編譯的 Fortran 程式碼所執行的線性代數。儘管無法保證,但目前在 64 位元平台上與 R 一起使用的所有編譯器似乎都允許每個維度小於 2^31 但具有 2^31 或更多元素的矩陣,並正確地為其編制索引,而且大部分支援軟體(例如 BLASLAPACK)也能正常運作。

有一些例外:例如,一些複雜的 LAPACK 輔助常式確實使用單一 INTEGER 索引,因此會靜默溢位並造成區段錯誤或產生錯誤的結果。所見的一個範例是在複雜矩陣上執行 svd()

由於這取決於實作,因此最佳化的 BLASLAPACK 可能會有進一步的限制:已報告使用 ATLAS 在「x86_64」Linux 上執行 svd() 時發生區段錯誤。

對於大型矩陣的矩陣代數,幾乎可以肯定需要一台具有大量 RAM(數百 GB)、多個核心和多執行緒 BLAS 的機器。


函數和變數索引

跳至:  _   .  
A   C   D   E   G   I   L   M   N   P   R   S   T   U   V   W  
索引條目區段

_
_R_CHECK_ALL_NON_ISO_C_工具
_R_CHECK_ALWAYS_LOG_VIGNETTE_OUTPUT_工具
_R_CHECK_ASCII_CODE_工具
_R_CHECK_ASCII_DATA_工具
_R_CHECK_AUTOCONF_工具
_R_CHECK_BASHISMS_工具
_R_CHECK_BOGUS_RETURN_工具
_R_CHECK_BROWSER_NONINTERACTIVE_工具
_R_CHECK_BUILD_VIGNETTES_ELAPSED_TIMEOUT_工具
_R_CHECK_BUILD_VIGNETTES_SEPARATELY_工具
_R_CHECK_CLEAN_VIGN_TEST_工具
_R_CHECK_CODE_ASSIGN_TO_GLOBALENV_工具
_R_CHECK_CODE_ATTACH_工具
_R_CHECK_CODE_CLASS_IS_STRING_工具
_R_CHECK_CODE_DATA_INTO_GLOBALENV_工具
_R_CHECK_CODE_USAGE_VIA_NAMESPACES_工具
_R_CHECK_CODE_USAGE_WITH_ONLY_BASE_ATTACHED_工具
_R_CHECK_CODETOOLS_PROFILE_工具
_R_CHECK_CODOC_S4_METHODS_工具
_R_CHECK_COMPACT_DATA_工具
_R_CHECK_COMPILATION_FLAGS_工具
_R_CHECK_CONNECTIONS_LEFT_OPEN_工具
_R_CHECK_CRAN_INCOMING_工具
_R_CHECK_CRAN_INCOMING_REMOTE_工具
_R_CHECK_DATALIST_工具
_R_CHECK_DEPENDS_ONLY_工具
_R_CHECK_DEPENDS_ONLY_DATA_工具
_R_CHECK_DEPENDS_ONLY_EXAMPLES_工具
_R_CHECK_DEPENDS_ONLY_TESTS_工具
_R_CHECK_DEPENDS_ONLY_VIGNETTES_工具
_R_CHECK_DEPRECATED_DEFUNCT_工具
_R_CHECK_DOC_SIZES_工具
_R_CHECK_DOC_SIZES2_工具
_R_CHECK_DONTTEST_EXAMPLES_工具
_R_CHECK_DOT_FIRSTLIB_工具
_R_CHECK_DOT_INTERNAL_工具
_R_CHECK_ELAPSED_TIMEOUT_工具
_R_CHECK_EXAMPLE_TIMING_CPU_TO_ELAPSED_THRESHOLD_工具
_R_CHECK_EXAMPLE_TIMING_THRESHOLD_工具
_R_CHECK_EXAMPLES_ELAPSED_TIMEOUT_工具
_R_CHECK_EXCESSIVE_IMPORTS_工具
_R_CHECK_EXECUTABLES_工具
_R_CHECK_EXECUTABLES_EXCLUSIONS_工具
_R_CHECK_EXIT_ON_FIRST_ERROR_工具
_R_CHECK_FF_CALLS_工具
_R_CHECK_FF_DUP_工具
_R_CHECK_FORCE_SUGGESTS_工具
_R_CHECK_FUTURE_FILE_TIMESTAMPS_工具
_R_CHECK_GCT_N_工具
_R_CHECK_INSTALL_DEPENDS_工具
_R_CHECK_INSTALL_ELAPSED_TIMEOUT_工具
_R_CHECK_LENGTH_1_CONDITION_工具
_R_CHECK_LICENSE_工具
_R_CHECK_LIMIT_CORES_工具
_R_CHECK_MATRIX_DATA_工具
_R_CHECK_NATIVE_ROUTINE_REGISTRATION_工具
_R_CHECK_NEWS_IN_PLAIN_TEXT_工具
_R_CHECK_NO_RECOMMENDED_工具
_R_CHECK_NO_STOP_ON_TEST_ERROR_工具
_R_CHECK_ONE_TEST_ELAPSED_TIMEOUT_工具
_R_CHECK_ONE_VIGNETTE_ELAPSED_TIMEOUT_工具
_R_CHECK_ORPHANED_工具
_R_CHECK_OVERWRITE_REGISTERED_S3_METHODS_工具
_R_CHECK_PACKAGES_USED_IN_TESTS_USE_SUBDIRS_工具
_R_CHECK_PERMISSIONS_工具
_R_CHECK_PKG_SIZES_工具
_R_CHECK_PKG_SIZES_THRESHOLD_工具
_R_CHECK_PKGMAN_ELAPSED_TIMEOUT_工具
_R_CHECK_PRAGMAS_工具
_R_CHECK_R_DEPENDS_工具
_R_CHECK_R_ON_PATH_工具
_R_CHECK_RD_ALLOW_EMPTY_ITEM_IN_DESCRIBE_工具
_R_CHECK_RD_CHECKRD_MINLEVEL_工具
_R_CHECK_RD_CONTENTS_工具
_R_CHECK_RD_EXAMPLES_T_AND_F_工具
_R_CHECK_RD_LINE_WIDTHS_工具
_R_CHECK_RD_MATH_RENDERING_工具
_R_CHECK_RD_STYLE_工具
_R_CHECK_RD_VALIDATE_RD2HTML_工具
_R_CHECK_RD_XREFS_工具
_R_CHECK_REPLACING_IMPORTS_工具
_R_CHECK_SCREEN_DEVICE_工具
_R_CHECK_SERIALIZATION_工具
_R_CHECK_SHLIB_OPENMP_FLAGS_工具
_R_CHECK_SKIP_ARCH_工具
_R_CHECK_SKIP_EXAMPLES_ARCH_工具
_R_CHECK_SKIP_TESTS_ARCH_工具
_R_CHECK_SRC_MINUS_W_IMPLICIT_工具
_R_CHECK_SRC_MINUS_W_UNUSED_工具
_R_CHECK_SUBDIRS_NOCASE_工具
_R_CHECK_SUBDIRS_STRICT_工具
_R_CHECK_SUGGESTS_ONLY_工具
_R_CHECK_SYSTEM_CLOCK_工具
_R_CHECK_TEST_TIMING_CPU_TO_ELAPSED_THRESHOLD_工具
_R_CHECK_TESTS_ELAPSED_TIMEOUT_工具
_R_CHECK_TESTS_NLINES_工具
_R_CHECK_THINGS_IN_CHECK_DIR_工具
_R_CHECK_THINGS_IN_OTHER_DIRS_工具
_R_CHECK_THINGS_IN_TEMP_DIR_工具
_R_CHECK_TIMINGS_工具
_R_CHECK_TOPLEVEL_FILES_工具
_R_CHECK_UNSAFE_CALLS_工具
_R_CHECK_USE_CODETOOLS_工具
_R_CHECK_USE_INSTALL_LOG_工具
_R_CHECK_VC_DIRS_工具
_R_CHECK_VIGNETTE_TIMING_CPU_TO_ELAPSED_THRESHOLD_工具
_R_CHECK_VIGNETTES_NLINES_工具
_R_CHECK_VIGNETTES_SKIP_RUN_MAYBE_工具
_R_CHECK_WALL_FORTRAN_工具
_R_CHECK_WINDOWS_DEVICE_工具
_R_CHECK_XREFS_MIND_SUSPECT_ANCHORS_工具
_R_CHECK_XREFS_PKGS_ARE_DECLARED_工具
_R_CHECK_XREFS_REPOSITORIES_工具
_R_CHECK_XREFS_USE_ALIASES_FROM_CRAN_工具
_R_INSTALL_LIBS_ONLY_FORCE_DEPENDS_IMPORTS_工具
_R_SHLIB_BUILD_OBJECTS_SYMBOL_TABLES_工具
_R_WIN_CHECK_INVALID_PARAMETERS_工具

.
.Device基本環境
.Devices基本環境
.Internal.Internal vs .Primitive
.Last.value基本環境
.Options基本環境
.Primitive.Internal vs .Primitive
.Random.seed全域環境
.SavedPlots全域環境
.Traceback基本環境

A
alloca記憶體配置器
ARGSUSED標頭的其餘部分
ATTRIB屬性
attribute_hidden隱藏 C 進入點

C
copyMostAttrib屬性

D
DDVAL標頭的其餘部分
debug bit標頭的其餘部分
DispatchGeneric參數評估
DispatchOrEval參數評估
dump.frames全域環境
DUPLICATE_ATTRIB屬性

E
emacsR coding standards
error警告和錯誤
errorcall警告和錯誤

G
gp bits標頭的其餘部分

I
invisible自動列印

L
last.warning基本環境
LEVELS標頭的其餘部分

M
makeR coding standards
makeinfoR coding standards
MISSING標頭的其餘部分
MISSING遺失值
mkCharCHARSXP 快取
mkCharLenCECHARSXP 快取

N
NAMED標頭的其餘部分
NAMED參數評估
NAMED.Internal vs .Primitive
named bits標頭的其餘部分

P
PerlR coding standards
PRIMPRINT自動列印
PRSEEN標頭的其餘部分

R
R_alloc記憶體配置器
R_AllocStringBuffer記憶體配置器
R_BaseNamespace命名空間
R_Calloc記憶體配置器
R_CheckStack記憶體配置器
R_CheckStack2記憶體配置器
R_Free記憶體配置器
R_FreeStringBuffer記憶體配置器
R_FreeStringBufferL記憶體配置器
R_MissingArg遺失值
R_Realloc記憶體配置器
R_Visible自動列印
Rdll.hide隱藏 C 進入點

S
SET_ARGUSED標頭的其餘部分
SET_ATTRIB屬性
SET_DDVAL標頭的其餘部分
SET_MISSING標頭的其餘部分
SET_NAMED標頭的其餘部分
SETLEVELS標頭的其餘部分
備用位元標頭的其餘部分

T
追蹤位元標頭的其餘部分

U
UseMethod內容

V
vmaxget記憶體配置器
vmaxset記憶體配置器

W
警告警告和錯誤
warningcall警告和錯誤


概念索引


腳註

(1)

嚴格來說,SEXPREC 節點;VECTOR_SEXPREC 節點稍小,但後面會接續節點中的資料。

(2)

指向函式的指標或用於按名稱查詢函式的符號,或要評估為函式的語言物件。

(3)

目前唯一的用途是環境雜湊表 (VECSXP),其中 length 是表格大小,truelength 是正在使用的主要槽位數,用於序列化中的參考雜湊表 (VECSXP),以及「可成長」向量(原子向量、VECSXPEXPRSXP),這些向量是在子指派期間擴充向量時透過略微超額提交來建立的,這樣在子指派期間後續的一些擴充就可以就地執行),其中 truelength 是正在使用的槽位數。

(4)

請記住,附加清單或儲存的映像實際上會建立並填入環境,並附加該環境。

(5)

目前還有另一個差異:在分析時,內建函式會計入函式呼叫,但特殊函式則不會。

(6)

另一個目前的範例是大括弧,它實作為一個基本型。

(7)

目前僅使用位元 0:4 來表示 SEXPTYPE,但值 241:255 則用於偽 SEXPTYPE

(8)

目前僅相關的位元為 0:1、4、14:15。

(9)

請參閱檔案 src/main/gram.c 舊版本中的定義 USE_UTF8_IF_POSSIBLE

(10)

或 UTF-16(如果在作業系統中啟用代理支援,它在 R 加入編碼支援時並未啟用)。

(11)

但 GraphApp 工具組則不會。

(12)

這也可以建立非 S4 物件,例如 new("integer")

(13)

雖然不建議這樣做,因為它較不具備未來性。

(14)

但顯然在 Windows 上不行。

(15)

C 程式碼位於目錄 src/main 中的檔案 base.cgraphics.cpar.cplot.cplot3d.c 中。

(16)

雖然這需要小心處理,例如 circle 回呼會給定一個半徑(且應將其解釋為 x 單位)。

(17)

裝置有可能找到指向其 DevDescGEDevDesc,而且這很常執行,因此有一個方便的函式 desc2GEDesc 可以執行此動作。

(18)

呼叫 R_CheckDeviceAvailable() 可確保有一個可用插槽,或擲回一個錯誤。

(19)

在裝置座標中

(20)

在技術上,可以在像印表機這類的圖形檔案裝置上使用 alpha 混合,但似乎很少有驅動程式支援這個功能。

(21)

一個 Xcode 專案,位於 SVN 中的 https://svn.r-project.org/R-packages/trunk/Mac-GUI/

(22)

在 Windows 下,連接點或複製,如果環境變數 R_WIN_NO_JUNCTIONS 有非空值。

(23)

常見的罪魁禍首是呼叫編譯程式碼 via .Call.External,它們會改變其引數。

(24)

位元組編譯器假設不會變更的事物,例如函式主體。

(25)

Linux 發行版傾向於從「texinfo」中取消捆綁 texinfo.tex

(26)

LENGTH 是某些內部使用中的巨集。