當前位置: 首頁>>代碼示例 >>用法及示例精選 >>正文


R ReferenceClasses 具有按引用處理的字段的對象(OOP 樣式)


R語言 ReferenceClasses 位於 methods 包(package)。

說明

此處說明的軟件允許包定義以 “OOP” 語言(例如 Java 和 C++)風格運行的引用類。此 OOP 模型不同於 S4(和 S3)類和方法實現的函數模型,其中方法是為泛型函數定義的。參考類的方法是類定義中的“encapsulated”。

使用引用類中的對象進行計算調用它們的方法並提取或設置它們的字段,使用`$`運算符在R。字段和方法計算可能會修改對象。與通常的函數式編程模型相比,引用對象的所有計算都會看到修改R.

在包的源代碼中調用 setRefClass 定義該類並返回一個生成器對象。對生成器的 $methods() 方法的後續調用將為該類定義方法。與函數類一樣,如果該類是從包中導出的,則在加載包時它將可用。

方法有R函數。在通常的實現中,它們直接通過名稱引用類的字段和其他方法。請參閱“Writing Reference Methods” 部分。

與函數類一樣,引用類可以通過 setRefClasscontains= 參數從其他引用類繼承。字段和方法將被繼承,除非新類覆蓋方法定義。請參閱“Inheritance” 部分。

用法

setRefClass(Class, fields = , contains = , methods =,
     where =, inheritPackage =, ...)

getRefClass(Class, where =)

參數

Class

類的字符串名稱。

在對 getRefClass() 的調用中,此參數也可以是相關類中的任何對象。

fields

字段名稱的字符向量或字段的命名列表。將使用引用語義訪問結果字段(請參閱“Reference Objects” 部分)。如果參數是一個列表,則列表中的每個元素通常應該是類的字符串名稱,在這種情況下,字段中的對象必須來自該類或子類。另一種選擇(但通常不推薦)是提供訪問器函數;有關訪問器函數和相關內部機製,請參閱“Implementation” 部分。

請注意,字段與槽不同。參考類不應定義class-specific 槽。請參閱 “Implementation” 部分中有關插槽的注釋。

contains

此類的可選超類向量。如果超類也是引用類,則將繼承字段和基於類的方法。

methods

可以在此類的對象上調用的函數定義的命名列表。這些也可以通過在返回的生成器對象上調用 $methods 方法來創建。有關詳細信息,請參閱“Writing Reference Methods” 部分。

where

對於 setRefClass ,存儲類定義的環境。在包源代碼的調用中應省略。

對於 getRefClass ,從中搜索定義的環境。如果包未加載或者您需要具體說明,請使用 asNamespace 和包名稱。

inheritPackage

新類中的對象是否應該繼承所包含的超類的包環境?默認FALSE。請參閱“Inter-Package超類和外部方法”部分。

...

其他參數傳遞給 setClass

setRefClass() 返回一個適合從類中隱式創建對象的生成器函數。對此函數的調用需要任意數量的參數,這些參數將傳遞給初始化方法。如果沒有為類或其超類之一定義 initialize 方法,則默認方法需要具有字段之一名稱的命名參數和來自此類超類之一的對象的未命名參數(如果有)(但隻有本身是引用類的超類才有效)。

生成器函數類似於 setClass 返回的 S4 生成器函數。然而,除了作為生成器函數之外,它還是一個引用類生成器對象,具有各種實用程序的引用類方法。請參閱下麵有關引用類生成器對象的部分。

getRefClass() 還返回該類的生成器函數。請注意,無論 where 參數如何,值中的包槽都是類定義中的正確包,該參數僅用於在必要時查找類。

參考對象

普通物體在R在函數調用中作為參數傳遞,與函數式編程語義一致;也就是說,對作為參數傳遞的對象所做的更改是函數調用的本地更改。提供參數的對象未更改。

函數模型(有時稱為pass-by-value,盡管這對於R)適用於許多統計計算並且是隱式的,例如,在基本計算中R用於擬合統計模型的軟件。在其他一些情況下,人們希望處理對象的所有代碼都看到完全相同的內容,以便任何計算中所做的更改都會反映在各處。如果對象具有一些“objective”現實,例如用戶接口中的窗口,這通常是合適的。

此外,常用語言(包括 Java、C++ 和許多其他語言)都支持假定引用語義的類和方法的版本。相應的編程機製是調用對象上的方法。在裏麵R我們使用的語法"$"對於此操作;一個調用一個方法,m1比如說,在一個物體上x通過表達式x$m1(...).

此範例中的方法與對象相關聯,或者更準確地說與對象的類相關聯,這與基於函數的類/方法係統中的方法相反,後者從根本上與函數相關聯(在R,例如,一個泛型函數Rsession 有一個包含所有當前已知方法的表)。在本文檔中,“類的方法”與“函數的方法”將進行區分。

此範例中的對象通常具有方法操作的命名字段。在裏麵R實現中,字段是在創建類時定義的。字段本身可以選擇具有指定的類,這意味著隻有該類或其子類之一的對象才能分配給該字段。默認情況下,字段有類"ANY".

通過引用訪問字段。特別地,調用方法可以修改字段的內容。

對此類的編程涉及為特定類編寫新方法。在裏麵R實現,這些方法是R具有零個或多個形式參數的函數。對於標準引用方法,對象本身不是該方法的顯式參數。相反,可以通過方法定義中的名稱來引用類的字段和方法。實現使用R環境,使字段和其他方法可以通過方法中的名稱使用。具體來說,方法的父環境是對象本身。請參閱“Writing Reference Methods” 部分。這種特殊的使用環境是可選的。如果一個方法是用初始形式參數定義的.self,它將作為整個對象傳入,並且該方法遵循包中任何函數的標準規則。請參閱“External Methods” 部分

這裏說明的軟件的目標是提供統一的編程風格R對於處理引用類的軟件,是否直接在R或者通過一種 OOP 語言的接口。

編寫參考方法

引用方法是作為命名列表元素提供的函數,可以在生成器對象 g 上調用 $methods() 時提供,也可以作為調用 setRefClass 中的參數 methods 提供。這兩種機製具有相同的效果,但第一種機製使代碼更具可讀性。

方法寫得和普通的一樣R函數,但在其通常形式下有一些特殊函數和限製。與其他一些語言(例如 Python)相比,對象本身不需要作為方法定義中的參數。函數體可以包含對任何其他引用方法的調用,包括從其他引用類繼承的方法,並且可以按名稱引用對象中的方法和字段。

或者,方法可以是外部方法。這是通過 .self 作為該方法的第一個正式參數來表示的。該方法的主體就像任何普通函數一樣工作。這些方法的調用方式與其他方法類似(沒有 .self 參數,該參數在內部提供,並且始終引用對象本身)。在方法內部,字段和其他方法以 .self$x 的形式訪問。外部方法的存在使得引用類可以繼承其他包中超類的包環境;請參閱“External Methods” 部分。

可以使用非局部賦值運算符在方法中修改字段,<<-,如在$edit$undo下麵例子中的方法。請注意,需要非本地分配:本地分配<-運算符隻是在函數調用中創建一個本地對象,就像在任何函數中一樣R函數。安裝方法時,將對字段名稱的本地分配進行啟發式檢查,並在檢測到任何情況時發出警告。

參考方法應保持簡單;如果他們需要做一些專門的事情R計算,該計算應該使用單獨的R從引用方法調用的函數。具體來說,方法不能使用封閉環境機製的特殊函數,因為方法的環境用於訪問字段和其他方法。特別是,方法不應使用包的命名空間中的非導出條目,因為這些方法可能會被另一個包中的引用類繼承。

兩個方法名稱被特殊解釋, initializefinalize 。如果定義了 initialize 方法,則當從類生成對象時將調用該方法。請參閱“Initialization Methods” 部分中對方法$new(...) 的討論。

如果一個finalize方法被定義,函數將是base reg.finalizer在對象中的環境被垃圾Collector丟棄之前調用它;終結器已注冊atexit=TRUE,所以也在末尾運行R會議。有關初始化和終結方法,請參閱矩陣查看器示例。

引用方法本身不能是泛型函數;如果您需要其他基於函數的方法分派,請編寫一個單獨的通用函數並從該方法中調用它。

有兩個特殊的對象名稱可用。整個對象可以在方法中通過保留名稱 .self 引用。對象.refClassDef包含該對象的類的定義。這些可以作為字段訪問,但都是隻讀的,但有一個例外。原則上,.self 字段可以在$initialize 方法中修改,因為此階段對象仍在創建中。不建議這樣做,因為它會使對象相對於其類無效。

可用的方法包括從超類繼承的方法,如“Inheritance”部分中討論的。

隻有實際使用的方法才會包含在與單個對象相對應的環境中。要聲明某個方法需要特定的其他方法,第一個方法應包含對 $usingMethods() 的調用,並以其他方法的名稱作為參數。如果間接使用其他方法(例如,通過 sapply()do.call() ),則以這種方式聲明方法至關重要。如果直接調用的話,代碼分析就能找到。然而,聲明該方法在任何情況下都是無害的,並且可能有助於源代碼的可讀性。

該方法的文檔可以通過以下方式獲得:$help生成器對象的方法。類的方法沒有記錄在Rd格式用於R函數。相反,$helpmethod 以Python 的風格打印方法的調用順序,後跟方法定義中的self-documentation。如果方法主體的第一個元素是文字字符串(可能是多行),則該字符串將被解釋為文檔。請參閱示例中的方法定義。

初始化方法

如果該類具有為 $initialize() 定義的方法,則在創建引用對象後將調用該方法。您應該為需要進行一些特殊初始化的類編寫這樣的方法。特別是,建議使用引用方法,而不是 S4 通用函數 initialize() 的方法,因為在初始化字段之前,引用對象需要進行一些特殊的初始化。與 S4 類一樣,方法是為 $initialize() 而不是為 $new() 編寫的,這既是出於前麵的原因,也是因為 $new() 在生成器對象上調用,並且將是該類的方法。

$initialize() 的默認方法相當於調用方法 $initFields(...) 。命名參數將初始值分配給相應的字段。未命名參數必須是此類的對象或此類的引用超類。字段將被初始化為此類對象中字段的內容,但命名參數會覆蓋相應的繼承字段。請注意,字段隻是分配的。如果字段本身是引用對象,則不會複製該對象。新的和以前的對象將共享引用。此外,從未命名參數分配的字段也算作鎖定字段的分配。要覆蓋鎖定字段的繼承值,新值必須是初始化調用中的命名參數之一。稍後分配該字段將導致錯誤。

初始化方法在設計時需要小心。引用類的生成器將在不帶參數的情況下調用,例如在複製對象時。為了確保這些調用不會失敗,該方法必須具有所有參數的默認值或檢查 missing() 。該方法應包含 ... 作為參數,並通過 $callSuper() 傳遞(或 $initFields(),如果您知道您的超類沒有初始化方法)。這允許將來的類定義使用附加字段來子類化此類。

繼承

參考類通過使用標準從其他參考類繼承R遺產;也就是說,通過將超類包含在contains=創建新類時的參數。參考超類的名稱位於槽中refSuperClasses類的定義。引用類也可以繼承普通的 S4 類,但如果混合引用字段和非引用槽,這通常是一個壞主意。請參閱“Implementation” 部分中的評論。

類字段是繼承的。僅當重寫類是繼承字段的類的子類時,類定義才可以重寫超類中的同名字段。這確保了字段中的有效對象對於超類也保持有效。

繼承的方法的安裝方式與直接指定的方法相同。方法中的代碼可以像直接指定方法一樣引用繼承的方法。

方法可以覆蓋超類中的同名方法。重寫方法可以通過 callSuper(...) 調用超類方法,如下所述。

為所有對象提供的方法

所有參考類都繼承自類 "envRefClass" 。所有引用對象都可以使用以下方法。

$callSuper(...)

調用從引用超類繼承的方法。該調用僅在另一個方法中才有意義,並且將解析為調用同名的繼承方法。 $callSuper 的參數被傳遞給超類版本。請參閱示例中的矩陣查看器類。

請注意,必須顯式提供超類方法的預期參數;與函數方法的類似機製相比,沒有自動提供參數的約定。

$copy(shallow = FALSE)

創建對象的副本。有參考類,與普通類不同R對象,僅僅為對象分配不同的名稱並不會創建獨立的副本。如果shallowFALSE,任何本身就是引用對象的字段也將被複製,並且對其字段進行類似的遞歸複製。否則,雖然將字段重新分配給新的引用對象不會產生副作用,但修改此類字段仍將反映在該對象的兩個副本中。該參數對字段中的非引用對象沒有影響。當某些字段存在引用對象但斷言它們不會被修改時,使用shallow = TRUE將節省一些內存和時間。

$field(name, value)

通過一個參數,返回字符串 name 的對象字段。對於兩個參數,相應的字段被分配為 value 。賦值檢查 name 是否指定了有效字段,但 single-argument 版本將嘗試從對象環境中獲取該名稱的任何內容。

當必須計算字段名稱或循環多個字段時,$field() 方法取代了直接使用字段名稱。

$export(Class)

返回將對象強製為Class(通常是對象類的超類之一)的結果。調用該方法對對象本身沒有副作用。

$getRefClass(); $getClass()

它們分別有效地返回生成器對象和該對象的引用類的正式類定義。

$import(value, Class = class(value))

將對象value導入到當前對象中,替換當前對象中的相應字段。對象 value 必須來自當前對象類的超類之一。如果提供了參數 Class,則首先將 value 強製為該類。

$initFields(...)

從提供的參數初始化對象的字段。通常僅從具有 $initialize() 方法的類調用此方法。它對應於引用類的默認初始化。如果存在槽和非引用超類,這些也可以在 ... 參數中提供。

通常,專門的 $initialize() 方法會執行自己的計算,然後調用 $initFields() 來執行標準初始化,如下麵示例中的 matrixViewer 類所示。

$show()

自動打印對象時會調用此方法,類似於show 函數。為類 "envRefClass" 定義了通用方法。用戶定義的引用類通常會定義自己的方法:請參見下麵的示例。

請注意示例中的兩點。與任何 show() 方法一樣,最好顯式打印該類以允許子類使用該方法。其次,要從方法中調用函數 show(),而不是調用 $show() 方法本身,請顯式引用 methods::show()

$trace(what, ...) , $untrace(what)

trace 函數的跟蹤和調試函數應用於參考方法 what

可以提供 trace 函數的所有參數,但 signature 除外,該參數沒有意義。

可以在對象或類的生成器上調用引用方法。有關詳細信息,請參閱下麵的“調試”部分。

$usingMethods(...)

此方法使用的引用方法被命名為帶引號或不帶引號的參數。在安裝本方法的代碼分析階段,將包含聲明的方法。必須聲明以非標準方式使用的任何方法(例如,通過 apply 函數)。直接調用的方法不需要聲明,但這樣做是無害的。 $usingMethods() 在運行時不執行任何操作。

對象還繼承兩個保留字段:

.self

對整個對象的引用;

.refClassDef

類定義。

定義的字段不應覆蓋這些字段,通常定義名稱以 "." 開頭的字段是不明智的,因為實現可能會將此類名稱用於特殊目的。

外部方法; Inter-Package 超類

引用類中方法的環境是對象本身,作為一個環境。這允許該方法直接引用字段和其他方法,而無需使用整個對象和 "$" 運算符。該環境的父環境是定義引用類的包的命名空間。該方法中的計算可以訪問包命名空間中的所有對象(無論是否導出)。

當定義一個包含另一個包中的引用超類的類時,哪個包命名空間應該具有該角色是不明確的。參數 inheritPackagesetRefClass() 控製新對象的環境是應該從另一個包中的繼承類繼承,還是繼續從當前包的命名空間繼承。

如果超類是“lean”,方法很少,或者主要是為了支持一係列子類而存在,那麽最好繼續使用新包的環境。另一方麵,如果超類最初是作為獨立編寫的,則此選擇可能會使現有的超類方法無效。為了使超類方法繼續工作,它們必須僅使用其包中的導出函數,並且新包必須導入這些函數。

無論哪種方式,可能需要編寫一些方法,這些方法不采用引用類方法的標準模型,但其行為本質上與處理引用類對象的普通函數相同。

該機製是識別外部方法。外部方法被編寫為函數,其中第一個參數名為 .self 代表引用類對象。該函數作為引用類方法的定義提供。該方法將被自動調用,第一個參數是當前對象,其他參數(如果有)從實際調用傳遞。

由於外部方法是其包的源代碼中的普通函數,因此它可以訪問命名空間中的所有對象。引用類中的字段和方法必須以 .self$name 的形式引用。

如果由於某種原因您不想使用 .self 作為第一個參數,則可以將函數 f() 顯式轉換為 externalRefMethod(f) ,該函數返回類 "externalRefMethod" 的對象,該對象可以作為類。第一個參數仍然對應於整個對象。

可以為任何引用類提供外部方法,但除非需要,否則沒有明顯的優勢。它們需要編寫更多的工作,閱讀起來更困難,並且執行起來(稍微)慢一些。

注意:如果您是一個包的作者,其引用類可能在其他包中被子類化,則可以通過編寫僅使用包中導出函數的方法來完全避免這些問題,以便所有方法都可以在另一個包中工作導入你的。

參考類生成器

setRefClass 的調用定義指定的類並返回該類的 “generator function” 對象。該對象具有類 "refObjectGenerator" ;它通過"classGeneratorFunction"繼承自"function",並且可以被調用以從引用類生成新對象。

返回的對象也是一個引用類對象,盡管不是標準結構。它可用於以通常的方式調用引用方法和訪問字段,但它不是直接作為環境實現,而是有一個作為槽的輔助生成器對象,一個標準引用對象(類 "refGeneratorSlot" )。請注意,如果想使用子類擴展參考類生成器函數,則應通過子類化 "refGeneratorSlot" 而不是 "refObjectGenerator" 來完成。

這些字段是def(類定義)和className(類的字符串名稱)。方法從類生成對象,訪問引用方法的幫助,並為類定義新的引用方法。目前可用的方法有:

$new(...)

該方法相當於調用 setRefClass 返回的生成器函數。

$help(topic)

打印有關該主題的簡短幫助。識別的主題是參考方法名稱,無論是否引用。

打印的信息是該方法的調用序列,加上self-documentation(如果有)。引用方法可以將初始字符串或向量作為定義該方法的函數體中的第一個元素。如果是這樣,該字符串將被視為該方法的self-documentation(有關詳細信息,請參閱“Writing Reference Methods”部分)。

如果未給出主題或主題不是方法名稱,則打印類的定義。

$methods(...)

如果不帶參數,則返回此類的引用方法的名稱。使用一個字符串參數,返回該名稱的方法。

命名參數是方法定義,它們將安裝在類中,就像它們在 methods 參數中提供給 setRefClass() 一樣。為了使源代碼更清晰,建議以這種方式提供方法,而不是調用 setRefClass() 。有關詳細信息,請參閱“Writing Reference Methods” 部分。

類的所有方法都應該在定義該類的源代碼中定義,通常作為包的一部分。特別是,不能在具有命名空間的附加包中的類中重新定義方法:類方法檢查類定義的鎖定綁定。

新方法可以按名稱引用任何當前定義的方法(包括在 $methods() 調用中提供的其他方法)。請注意,先前定義的方法不會被重新分析,這意味著它們不會調用新方法(除非它重新定義同名的現有方法)。

要刪除方法,請提供NULL 作為其新定義。

$fields()

返回字段列表,每個字段都有其對應的類。在定義中為其提供訪問器函數的字段具有類 "activeBindingFunction"

$lock(...)

參數中指定的字段被鎖定;具體地,調用lock方法後,該字段可以被設置一次。任何進一步嘗試設置它都會生成錯誤。

如果不帶參數調用,該方法將返回鎖定字段的名稱。

由顯式訪問器函數定義的字段不能被鎖定(另一方麵,如果使用參數調用訪問器函數,則可以定義為生成錯誤)。

所有鎖定字段的代碼通常應該是類定義的一部分;也就是說,字段的隻讀性質是類定義的一部分,而不是稍後添加的動態屬性。特別是,字段不能鎖定在具有命名空間的附加包中的類中:類方法檢查類定義的鎖定綁定。鎖定的字段隨後無法解鎖。

$trace(what, ..., classMethod = FALSE)

為此類生成的對象建立方法 what 的跟蹤版本。生成器對象跟蹤的工作方式類似於類中對象的 $trace() 方法,但有兩個區別。由於它更改了類對象本身中的方法定義,因此跟蹤適用於所有對象,而不僅僅是調用跟蹤方法的對象。

其次,可選參數 classMethod = TRUE 允許跟蹤生成器對象本身的方法。默認情況下,what 被解釋為該對象作為生成器的類中的方法名稱。

$accessors(...)

許多使用 OOP 編程範式的係統推薦或強製執行與每個字段對應的 getter 和 setter 方法,而不是通過名稱直接訪問。如果您喜歡這種風格,並且想要通過 x$getAbc() 提取名為 abc 的字段並通過 x$setAbc(value) 對其進行分配,則 $accessors 方法是一個方便的函數,可以為指定字段創建此類 getter 和 setter 方法。否則沒有理由使用這個機製。特別是,它與 “Reference Objects” 部分中說明的通過函數定義字段的一般能力無關。

執行;參考類作為 S4 類

引用類被實現為 S4 類,其數據部分類型為 "environment" 。字段對應於環境中的命名對象。與函數關聯的字段被實現為 active binding 。特別是,具有指定類的字段被實現為主動綁定的特殊形式,以強製對該字段進行有效分配。

作為一項相關函數,提供給 setRefClassfields= 列表中的元素可以是訪問器函數,這是一種隻有一個參數的函數,如果不帶參數調用,則返回該字段,否則將其設置為參數的值。訪問器函數在內部使用並用於 inter-system 接口應用程序,但通常不推薦,因為它們模糊了字段作為對象內數據的概念。

一個領域,比如說data,通常可以通過以下形式的表達式來訪問x$data對於相關類中的任何對象。在此類的內部方法中,可以通過名稱訪問該字段data。未鎖定的字段可以通過以下形式的表達式設置x$data <- value。在內部方法內部,可以通過以下形式的表達式分配字段x <<- value。請注意base assignOps操作符。標準R該運算符的解釋用於將其分配到對象的環境中。如果該字段定義了訪問器函數,則獲取和設置將調用該函數。

當在對象上調用方法時,定義該方法的函數將安裝在該對象的環境中,並且環境與該函數的環境相同。

參考類可以具有與任何 S4 類相同意義上的有效性方法(請參閱setValidity)。這些方法通常是個好主意;他們將被調用validObject並且如果定義了一個有效性方法,則在創建引用對象時將調用該方法(從 3.4 版開始)R在)。請記住這些是 S4 方法。該函數將被調用object作為其論點。必須使用以下方式訪問字段和方法$.

注:插槽。由於實現的緣故,新的引用類可以從非引用 S4 類以及引用類繼承,並且可以在定義中包含class-specific 槽。如果非引用類中的槽被認為是字段的替代品,那麽這通常是一個壞主意。插槽將一如既往地按函數進行處理。因此,對槽和字段的更改將表現得不一致,混合同一對象屬性的函數和引用範例,概念上不清楚並且容易出錯。此外,該類的初始化方法必須從槽中整理字段,這很有可能為該類的子類創建異常行為。

然而,從 class union 繼承是一個合理的策略(聯合體的所有成員都可能是引用類)。

調試

標準R調試和跟蹤工具可應用於參考方法。參考方法可以傳遞給debug及其來自對象的親屬,以調試該對象上的進一步方法調用;例如,debug(xx$edit).

trace 函數的參考方法版本的使用更為靈活。相應的$trace() 引用方法可用於對象或引用類生成器(下例中的xx$trace()mEdit$trace())。在對象上使用 $trace() 會設置跟蹤版本,以便將來調用該對象的指定方法。在類的生成器上使用 $trace() 會為該類中的所有未來對象(如果未聲明或先前調用該方法,有時還會為該類中的現有對象)設置跟蹤版本。

在任何一種情況下,標準 trace 函數的所有參數都可用,但 signature= 除外,它沒有意義,因為引用方法不能是 S4 泛型函數。這包括用於交互式調試的典型樣式 trace(what, browser) 和用於交互式編輯參考方法的 trace(what, edit = TRUE)

例子

## a simple editor for matrix objects.  Method  $edit() changes some
## range of values; method $undo() undoes the last edit.
mEdit <- setRefClass("mEdit",
      fields = list( data = "matrix",
        edits = "list"))

## The basic edit, undo methods
mEdit$methods(
     edit = function(i, j, value) {
       ## the following string documents the edit method
       'Replaces the range [i, j] of the
        object by value.
        '
         backup <-
             list(i, j, data[i,j])
         data[i,j] <<- value
         edits <<- c(edits, list(backup))
         invisible(value)
     },
     undo = function() {
       'Undoes the last edit() operation
        and update the edits field accordingly.
        '
         prev <- edits
         if(length(prev)) prev <- prev[[length(prev)]]
         else stop("No more edits to undo")
         edit(prev[[1]], prev[[2]], prev[[3]])
         ## trim the edits list
         length(edits) <<- length(edits) - 2
         invisible(prev)
     })

## A method to automatically print objects
mEdit$methods(
     show = function() {
       'Method for automatically printing matrix editors'
       cat("Reference matrix editor object of class",
          classLabel(class(.self)), "\n")
       cat("Data: \n")
       methods::show(data)
       cat("Undo list is of length", length(edits), "\n")
     }
     )

xMat <- matrix(1:12,4,3)
xx <- mEdit(data = xMat)
xx$edit(2, 2, 0)
xx
xx$undo()
mEdit$help("undo")
stopifnot(all.equal(xx$data, xMat))

utils::str(xx) # show fields and names of methods

## A method to save the object
mEdit$methods(
     save = function(file) {
       'Save the current object on the file
        in R external object format.
       '
         base::save(.self, file = file)
     }
)

tf <- tempfile()
xx$save(tf)


## Not run: 
## Inheriting a reference class:  a matrix viewer
mv <- setRefClass("matrixViewer",
    fields = c("viewerDevice", "viewerFile"),
    contains = "mEdit",
    methods = list( view = function() {
        dd <- dev.cur(); dev.set(viewerDevice)
        devAskNewPage(FALSE)
        matplot(data, main = paste("After",length(edits),"edits"))
        dev.set(dd)},
        edit = # invoke previous method, then replot
          function(i, j, value) {
            callSuper(i, j, value)
            view()
          }))

## initialize and finalize methods
mv$methods( initialize =
  function(file = "./matrixView.pdf", ...) {
    viewerFile <<- file
    pdf(viewerFile)
    viewerDevice <<- dev.cur()
    dev.set(dev.prev())
    callSuper(...)
  },
  finalize = function() {
    dev.off(viewerDevice)
  })

## debugging an object: call browser() in method $edit()
xx$trace(edit, browser)

## debugging all objects from class mEdit in method $undo()
mEdit$trace(undo, browser)

## End(Not run)
 

參考

Chambers, John M. (2016) Extending R, Chapman & Hall. (Chapters 9 and 11.)

相關用法


注:本文由純淨天空篩選整理自R-devel大神的英文原創作品 Objects With Fields Treated by Reference (OOP-style)。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。