R Language
S4 क्लास का उपयोग करके स्टेट मशीन पैटर्न लागू करें
खोज…
परिचय
आमतौर पर ऑब्जेक्ट स्टेट ओरिएंटेड प्रोग्रामिंग (ओओपी) भाषाओं के तहत फिनिट स्टेट्स मशीन अवधारणाओं को लागू किया जाता है, उदाहरण के लिए जावा भाषा का उपयोग करते हुए , GOF में परिभाषित राज्य पैटर्न के आधार पर (पुस्तक का संदर्भ: "डिजाइन पैटर्न")।
आरओ प्रतिमान को अनुकरण करने के लिए कई तंत्र प्रदान करता है, आइए इस पैटर्न को लागू करने के लिए एस 4 ऑब्जेक्ट सिस्टम लागू करें।
राज्य मशीन का उपयोग कर पार्सिंग लाइनें
चलो आर से एस 4 क्लास सुविधा का उपयोग करके विशिष्ट पैटर्न के साथ पार्सिंग लाइनों के लिए स्टेट मशीन पैटर्न लागू करें।
समस्या निस्तारण
हमें एक फ़ाइल को पार्स करने की आवश्यकता है जहां प्रत्येक पंक्ति किसी व्यक्ति के बारे में जानकारी प्रदान करती है, एक सीमांकक ( ";"
) का उपयोग करते हुए, लेकिन प्रदान की गई कुछ जानकारी वैकल्पिक है, और खाली क्षेत्र प्रदान करने के बजाय, यह गायब है। प्रत्येक पंक्ति में हमारे पास निम्नलिखित जानकारी हो सकती है: Name;[Address;]Phone
। जहां पते की जानकारी वैकल्पिक है, कभी-कभी हमारे पास यह होता है और कभी-कभी उदाहरण के लिए नहीं होता है:
GREGORY BROWN; 25 NE 25TH; +1-786-987-6543
DAVID SMITH;786-123-4567
ALAN PEREZ; 25 SE 50TH; +1-786-987-5553
दूसरी पंक्ति पते की जानकारी नहीं देती है। इसलिए सीमांकक की संख्या इस मामले में भिन्न हो सकती है जैसे कि एक सीमांकक के साथ और दूसरी रेखाओं के लिए दो सीमांकक। क्योंकि सीमांकक की संख्या भिन्न हो सकती है, इस समस्या को कम करने का एक तरीका यह है कि किसी दिए गए क्षेत्र की उपस्थिति या उसके आधार पर पहचान न की जाए। ऐसे मामले में हम ऐसे पैटर्न की पहचान के लिए एक नियमित अभिव्यक्ति का उपयोग कर सकते हैं। उदाहरण के लिए:
- नाम :
"^([AZ]'?\\s+)* *[AZ]+(\\s+[AZ]{1,2}\\.?,? +)*[AZ]+((-|\\s+)[AZ]+)*$"
। उदाहरण के लिए:RAFAEL REAL, DAVID R. SMITH, ERNESTO PEREZ GONZALEZ, 0' CONNOR BROWN, LUIS PEREZ-MENA
, आदि। - पता :
"^\\s[0-9]{1,4}(\\s+[AZ]{1,2}[0-9]{1,2}[AZ]{1,2}|[AZ\\s0-9]+)$"
। उदाहरण के लिए:11020 LE JEUNE ROAD
,87 SW 27TH
। सादगी के लिए हम यहाँ ज़िपकोड, शहर, राज्य को शामिल नहीं करते हैं, लेकिन मुझे इस क्षेत्र में शामिल किया जा सकता है या अतिरिक्त फ़ील्ड जोड़ सकते हैं। - फ़ोन :
"^\\s*(\\+1(-|\\s+))*[0-9]{3}(-|\\s+)[0-9]{3}(-|\\s+)[0-9]{4}$"
। उदाहरण के लिए:305-123-4567, 305 123 4567, +1-786-123-4567
।
नोट :
- मैं अमेरिका के पते और फोन के सबसे सामान्य पैटर्न पर विचार कर रहा हूं, अधिक सामान्य स्थितियों पर विचार करने के लिए इसे बढ़ाया जा सकता है।
- R में संकेत
"\"
का वर्ण चर के लिए विशेष अर्थ है, इसलिए हमें इससे बचने की आवश्यकता है। - नियमित अभिव्यक्तियों को परिभाषित करने की प्रक्रिया को सरल बनाने के लिए निम्नलिखित वेब पेज का उपयोग करने के लिए एक अच्छी सिफारिश है: regex101.com , इसलिए आप इसे दिए गए उदाहरण के साथ खेल सकते हैं, जब तक कि आपको सभी संभावित संयोजनों के लिए अपेक्षित परिणाम नहीं मिल जाता।
विचार पहले परिभाषित पैटर्न के आधार पर प्रत्येक पंक्ति क्षेत्र की पहचान करना है। राज्य पैटर्न निम्नलिखित संस्थाओं (वर्गों) को परिभाषित करता है जो विशिष्ट व्यवहार को नियंत्रित करने के लिए सहयोग करते हैं (राज्य पैटर्न एक व्यवहार पैटर्न है):
आइए हमारी समस्या के संदर्भ में विचार करते हुए प्रत्येक तत्व का वर्णन करें:
-
Context
: पार्सिंग प्रक्रिया की संदर्भ जानकारी, अर्थात वर्तमान स्थिति को संग्रहीत करता है और संपूर्ण राज्य मशीन प्रक्रिया को संभालता है। प्रत्येक राज्य के लिए, एक क्रिया निष्पादित की जाती है (handle()
), लेकिन संदर्भ राज्य के आधार पर, किसी विशेष राज्य (State
वर्ग सेhandle()
लिए परिभाषित क्रिया विधि पर आधारित होता है। यह ग्राहकों के लिए रुचि के इंटरफेस को परिभाषित करता है। हमारेContext
वर्ग को इस तरह परिभाषित किया जा सकता है:- गुण:
state
- तरीके:
handle()
, ...
- गुण:
-
State
: सार वर्ग जो राज्य मशीन के किसी भी राज्य का प्रतिनिधित्व करता है। यह संदर्भ की एक विशेष स्थिति से जुड़े व्यवहार को एनकैप्सुलेट करने के लिए एक इंटरफ़ेस को परिभाषित करता है। इसे इस तरह परिभाषित किया जा सकता है:- विशेषताएँ:
name, pattern
- तरीके:
doAction()
,isState
(pattern
विशेषता का उपयोग करके सत्यापित करें कि इनपुट तर्क इस राज्य पैटर्न से संबंधित है या नहीं),…
- विशेषताएँ:
-
Concrete States
(राज्य उप-वर्गों): वर्ग के प्रत्येक उपवर्गState
कि औजार एक व्यवहार के एक राज्य के साथ जुड़ेContext
। हमारे उप-वर्गों हैं:InitState
,NameState
,AddressState
,PhoneState
। ऐसी कक्षाएं केवल ऐसे राज्यों के लिए विशिष्ट तर्क का उपयोग करके सामान्य पद्धति को लागू करती हैं। किसी अतिरिक्त विशेषता की आवश्यकता नहीं है।
नोट: यह प्राथमिकता का विषय है कि एक्शन, handle()
, doAction()
या goNext()
करने की विधि का नाम कैसे दिया goNext()
। विधि नाम doAction()
दोनों वर्गों ( State
या Context
) के लिए समान हो सकता है जिसे हमने एक ही इनपुट तर्कों के साथ दो सामान्य तरीकों को परिभाषित करते समय एक भ्रम से बचने के लिए Context
कक्षा में handle()
रूप में नाम देना पसंद किया, लेकिन अलग-अलग वर्ग।
निजी वर्ग
S4 सिंटैक्स का उपयोग करके हम इस तरह एक व्यक्ति वर्ग को परिभाषित कर सकते हैं:
setClass(Class = "Person",
slots = c(name = "character", address = "character", phone = "character")
)
यह क्लास विशेषताओं को इनिशियलाइज़ करने के लिए एक अच्छी सिफारिश है। setClass
प्रलेखन "initialize"
रूप में लेबल किए गए एक जेनेरिक विधि का उपयोग करने का सुझाव देता है, इसके बजाय जैसे: prototype, representation
।
setMethod("initialize", "Person",
definition = function(.Object, name = NA_character_,
address = NA_character_, phone = NA_character_) {
.Object@name <- name
.Object@address <- address
.Object@phone <- phone
.Object
}
)
क्योंकि प्रारंभिक विधि पहले से ही पैकेज methods
का एक मानक सामान्य तरीका है, हमें मूल तर्क परिभाषा का सम्मान करने की आवश्यकता है। हम इसे R प्रांप्ट पर टाइप करके सत्यापित कर सकते हैं:
> initialize
यह संपूर्ण फ़ंक्शन की परिभाषा देता है, आप शीर्ष पर देख सकते हैं कि फ़ंक्शन किस प्रकार परिभाषित किया गया है:
function (.Object, ...) {...}
इसलिए जब हम का उपयोग setMethod
हम exaclty एक ही वाक्य रचना (पालन करने की आवश्यकता .Object
)।
एक और मौजूदा सामान्य विधि है show
है, यह बराबर है toString()
जावा से विधि और यह वर्ग डोमेन के लिए एक विशिष्ट कार्यान्वयन के लिए एक अच्छा विचार है:
setMethod("show", signature = "Person",
definition = function(object) {
info <- sprintf("%s@[name='%s', address='%s', phone='%s']",
class(object), object@name, object@address, object@phone)
cat(info)
invisible(NULL)
}
)
नोट : हम उसी कन्वेंशन का उपयोग करते हैं जैसे कि डिफ़ॉल्ट toString()
जावा कार्यान्वयन में।
चलो कहते हैं कि हम पार्स जानकारी (की एक सूची सहेजना चाहते Person
एक डाटासेट में वस्तुओं), तो हम कुछ में करने के लिए वस्तुओं की एक सूची कन्वर्ट करने के लिए आर (एक सूची के रूप वस्तु उदाहरण विवश के लिए) बदल सकता है सक्षम पहले होना चाहिए। हम निम्नलिखित अतिरिक्त विधि को परिभाषित कर सकते हैं (इस बारे में अधिक विवरण के लिए पोस्ट देखें)
setGeneric(name = "as.list", signature = c('x'),
def = function(x) standardGeneric("as.list"))
# Suggestion taken from here:
# http://stackoverflow.com/questions/30386009/how-to-extend-as-list-in-a-canonical-way-to-s4-objects
setMethod("as.list", signature = "Person",
definition = function(x) {
mapply(function(y) {
#apply as.list if the slot is again an user-defined object
#therefore, as.list gets applied recursively
if (inherits(slot(x,y),"Person")) {
as.list(slot(x,y))
} else {
#otherwise just return the slot
slot(x,y)
}
},
slotNames(class(x)),
SIMPLIFY=FALSE)
}
)
R, OO के लिए एक चीनी वाक्य-विन्यास प्रदान नहीं करता है क्योंकि भाषा को आरंभ में सांख्यिकीविदों के लिए मूल्यवान कार्य प्रदान करने की कल्पना की गई थी। इसलिए प्रत्येक उपयोगकर्ता विधि को दो भागों की आवश्यकता होती है: 1) डेफिनिशन भाग ( setGeneric
माध्यम से) और 2) कार्यान्वयन भाग ( setMethod
माध्यम से)। जैसे ऊपर के उदाहरण में।
स्टेट क्लास
S4 सिंटैक्स के बाद, आइए सार State
वर्ग को परिभाषित करें।
setClass(Class = "State", slots = c(name = "character", pattern = "character"))
setMethod("initialize", "State",
definition = function(.Object, name = NA_character_, pattern = NA_character_) {
.Object@name <- name
.Object@pattern <- pattern
.Object
}
)
setMethod("show", signature = "State",
definition = function(object) {
info <- sprintf("%s@[name='%s', pattern='%s']", class(object),
object@name, object@pattern)
cat(info)
invisible(NULL)
}
)
setGeneric(name = "isState", signature = c('obj', 'input'),
def = function(obj, input) standardGeneric("isState"))
setGeneric(name = "doAction", signature = c('obj', 'input', 'context'),
def = function(obj, input, context) standardGeneric("doAction"))
State
प्रत्येक उप-वर्ग में एक name
और pattern
जुड़ा होगा, लेकिन यह भी पहचानने का एक तरीका है कि क्या दिया गया इनपुट इस राज्य से संबंधित है या नहीं ( isState()
विधि), और इस राज्य ( doAction()
लिए संबंधित क्रियाएं भी लागू करता है तरीका)।
प्रक्रिया को समझने के लिए, आइए प्राप्त इनपुट के आधार पर प्रत्येक राज्य के लिए संक्रमण मैट्रिक्स को परिभाषित करें:
इनपुट / करंट स्टेट | में इस | नाम | पता | फ़ोन |
---|---|---|---|---|
नाम | नाम | |||
पता | पता | |||
फ़ोन | फ़ोन | फ़ोन | ||
समाप्त | समाप्त |
नोट: सेल [row, col]=[i,j]
वर्तमान स्थिति j
लिए गंतव्य स्थिति का प्रतिनिधित्व करता है, जब यह इनपुट i
प्राप्त करता है।
इसका मतलब है कि राज्य के नाम के तहत इसे दो इनपुट मिल सकते हैं: एक पता या एक फोन नंबर। लेन-देन तालिका का प्रतिनिधित्व करने का एक अन्य तरीका निम्नलिखित यूएमएल स्टेट मशीन आरेख का उपयोग कर रहा है:
आइए प्रत्येक विशेष राज्य को वर्ग State
उप-राज्य के रूप में लागू करें
स्टेट सब-क्लास
Init राज्य :
प्रारंभिक राज्य को निम्न वर्ग के माध्यम से लागू किया जाएगा:
setClass("InitState", contains = "State")
setMethod("initialize", "InitState",
definition = function(.Object, name = "init", pattern = NA_character_) {
.Object@name <- name
.Object@pattern <- pattern
.Object
}
)
setMethod("show", signature = "InitState",
definition = function(object) {
callNextMethod()
}
)
आर को इंगित करने के लिए एक वर्ग अन्य वर्ग का उप-वर्ग है contains
विशेषता वर्ग का उपयोग किया जाता है और मूल वर्ग के वर्ग के नाम को दर्शाता है।
क्योंकि उप-वर्ग केवल अतिरिक्त विशेषताओं को जोड़े बिना, जेनेरिक विधियों को लागू करते हैं, तो show
विधि, बस ऊपरी वर्ग से समकक्ष विधि (विधि के माध्यम से: callNextMethod()
) को कॉल करें
प्रारंभिक स्थिति में एक पैटर्न जुड़ा नहीं है, यह सिर्फ प्रक्रिया की शुरुआत का प्रतिनिधित्व करता है, फिर हम NA
मान के साथ वर्ग को आरंभीकृत करते हैं।
अब State
वर्ग से सामान्य विधियों को लागू करने की अनुमति देता है:
setMethod(f = "isState", signature = "InitState",
definition = function(obj, input) {
nameState <- new("NameState")
result <- isState(nameState, input)
return(result)
}
)
इस विशेष स्थिति के लिए ( pattern
बिना), विचार यह सिर्फ पहले क्षेत्र की अपेक्षा पार्सिंग प्रक्रिया को शुरू करता है एक name
, अन्यथा यह एक त्रुटि होगी।
setMethod(f = "doAction", signature = "InitState",
definition = function(obj, input, context) {
nameState <- new("NameState")
if (isState(nameState, input)) {
person <- context@person
person@name <- trimws(input)
context@person <- person
context@state <- nameState
} else {
msg <- sprintf("The input argument: '%s' cannot be identified", input)
stop(msg)
}
return(context)
}
)
doAction
पद्धति संक्रमण प्रदान करती है और निकाले गए जानकारी के साथ संदर्भ को अपडेट करती है। यहां हम @-operator
माध्यम से संदर्भ जानकारी तक पहुंच रहे हैं। इसके बजाय, हम इस प्रक्रिया को इनकैप्सुलेट करने के get/set
विधियों को परिभाषित कर सकते हैं (जैसा कि यह OO सर्वोत्तम प्रथाओं में अनिवार्य है: एनकैप्सुलेशन), लेकिन यह इस उदाहरण के उद्देश्य के लिए मूल्य को जोड़े बिना गेट get-set
चार और तरीकों को जोड़ देगा।
यह सभी doAction
कार्यान्वयन में एक अच्छा सुझाव है, जब इनपुट तर्क को ठीक से पहचाना नहीं जाता है, तो एक सुरक्षा गार्ड जोड़ना।
नाम राज्य
इस वर्ग की परिभाषा इस प्रकार है:
setClass ("NameState", contains = "State")
setMethod("initialize","NameState",
definition=function(.Object, name="name",
pattern = "^([A-Z]'?\\s+)* *[A-Z]+(\\s+[A-Z]{1,2}\\.?,? +)*[A-Z]+((-|\\s+)[A-Z]+)*$") {
.Object@pattern <- pattern
.Object@name <- name
.Object
}
)
setMethod("show", signature = "NameState",
definition = function(object) {
callNextMethod()
}
)
हम इनपुट को सत्यापित करने के लिए फ़ंक्शन grepl
उपयोग किसी दिए गए पैटर्न से करते हैं।
setMethod(f="isState", signature="NameState",
definition=function(obj, input) {
result <- grepl(obj@pattern, input, perl=TRUE)
return(result)
}
)
अब हम दिए गए राज्य के लिए कार्रवाई करने को परिभाषित करते हैं:
setMethod(f = "doAction", signature = "NameState",
definition=function(obj, input, context) {
addressState <- new("AddressState")
phoneState <- new("PhoneState")
person <- context@person
if (isState(addressState, input)) {
person@address <- trimws(input)
context@person <- person
context@state <- addressState
} else if (isState(phoneState, input)) {
person@phone <- trimws(input)
context@person <- person
context@state <- phoneState
} else {
msg <- sprintf("The input argument: '%s' cannot be identified", input)
stop(msg)
}
return(context)
}
)
यहां हम संभावित बदलावों पर विचार करते हैं: एक पता राज्य के लिए और दूसरा फोन राज्य के लिए। सभी मामलों में हम संदर्भ जानकारी को अपडेट करते हैं:
-
person
जानकारी: इनपुट तर्क के साथaddress
याphone
। - प्रक्रिया की
state
राज्य की पहचान करने का तरीका विधि को लागू करना है: किसी विशेष राज्य के लिए isState()
। हम एक डिफ़ॉल्ट विशिष्ट स्थिति ( addressState, phoneState
) addressState, phoneState
और फिर एक विशेष सत्यापन के लिए पूछते हैं।
अन्य उप-वर्गों (प्रति राज्य एक) कार्यान्वयन के लिए तर्क बहुत समान है।
पता राज्य
setClass("AddressState", contains = "State")
setMethod("initialize", "AddressState",
definition = function(.Object, name="address",
pattern = "^\\s[0-9]{1,4}(\\s+[A-Z]{1,2}[0-9]{1,2}[A-Z]{1,2}|[A-Z\\s0-9]+)$") {
.Object@pattern <- pattern
.Object@name <- name
.Object
}
)
setMethod("show", signature = "AddressState",
definition = function(object) {
callNextMethod()
}
)
setMethod(f="isState", signature="AddressState",
definition=function(obj, input) {
result <- grepl(obj@pattern, input, perl=TRUE)
return(result)
}
)
setMethod(f = "doAction", "AddressState",
definition=function(obj, input, context) {
phoneState <- new("PhoneState")
if (isState(phoneState, input)) {
person <- context@person
person@phone <- trimws(input)
context@person <- person
context@state <- phoneState
} else {
msg <- sprintf("The input argument: '%s' cannot be identified", input)
stop(msg)
}
return(context)
}
)
फोन की स्थिति
setClass("PhoneState", contains = "State")
setMethod("initialize", "PhoneState",
definition = function(.Object, name = "phone",
pattern = "^\\s*(\\+1(-|\\s+))*[0-9]{3}(-|\\s+)[0-9]{3}(-|\\s+)[0-9]{4}$") {
.Object@pattern <- pattern
.Object@name <- name
.Object
}
)
setMethod("show", signature = "PhoneState",
definition = function(object) {
callNextMethod()
}
)
setMethod(f = "isState", signature = "PhoneState",
definition = function(obj, input) {
result <- grepl(obj@pattern, input, perl = TRUE)
return(result)
}
)
यहां हम उस व्यक्ति की जानकारी को context
के persons
की सूची में जोड़ते हैं।
setMethod(f = "doAction", "PhoneState",
definition = function(obj, input, context) {
context <- addPerson(context, context@person)
context@state <- new("InitState")
return(context)
}
)
अगला विषय
अब Context
वर्ग कार्यान्वयन की व्याख्या करने की सुविधा देता है। हम निम्नलिखित विशेषताओं को देखते हुए इसे परिभाषित कर सकते हैं:
setClass(Class = "Context",
slots = c(state = "State", persons = "list", person = "Person")
)
कहाँ पे
-
state
: प्रक्रिया की वर्तमान स्थिति -
person
: वर्तमान व्यक्ति, यह उस सूचना का प्रतिनिधित्व करता है जिसे हमने पहले ही वर्तमान लाइन से पार्स कर दिया है। -
persons
: संसाधित व्यक्तियों की सूची।
नोट : वैकल्पिक रूप से, हम एक से अधिक प्रकार के पार्सर के साथ काम कर रहे मामले में नाम से संदर्भ की पहचान करने के लिए एक name
जोड़ सकते हैं।
setMethod(f="initialize", signature="Context",
definition = function(.Object) {
.Object@state <- new("InitState")
.Object@persons <- list()
.Object@person <- new("Person")
return(.Object)
}
)
setMethod("show", signature = "Context",
definition = function(object) {
cat("An object of class ", class(object), "\n", sep = "")
info <- sprintf("[state='%s', persons='%s', person='%s']", object@state,
toString(object@persons), object@person)
cat(info)
invisible(NULL)
}
)
setGeneric(name = "handle", signature = c('obj', 'input', 'context'),
def = function(obj, input, context) standardGeneric("handle"))
setGeneric(name = "addPerson", signature = c('obj', 'person'),
def = function(obj, person) standardGeneric("addPerson"))
setGeneric(name = "parseLine", signature = c('obj', 's'),
def = function(obj, s) standardGeneric("parseLine"))
setGeneric(name = "parseLines", signature = c('obj', 's'),
def = function(obj, s) standardGeneric("parseLines"))
setGeneric(name = "as.df", signature = c('obj'),
def = function(obj) standardGeneric("as.df"))
इस तरह के सामान्य तरीकों से, हम पार्सिंग प्रक्रिया के संपूर्ण व्यवहार को नियंत्रित करते हैं:
-
handle()
: वर्तमानstate
के विशेषdoAction()
विधि को लागू करेगा। -
addPerson
: एक बार जब हम अंतिम स्थिति में पहुंच जाते हैं, तो हमें एकperson
को उनpersons
की सूची में जोड़ने की आवश्यकता होती है जिन्हें हमने पार्स किया है। -
parseLine()
: सिंगल लाइन को पार्स करें -
parseLines()
: कई लाइनों कोparseLines()
लाइनों की एक सरणी) -
as.df()
:persons
सूची से जानकारी को डेटा फ्रेम ऑब्जेक्ट में निकालें।
आइए अब इसी कार्यान्वयन के साथ चलते हैं:
handle()
विधि, doAction()
context
की वर्तमान state
से विधि पर प्रतिनिधियों:
setMethod(f = "handle", signature = "Context",
definition = function(obj, input) {
obj <- doAction(obj@state, input, obj)
return(obj)
}
)
setMethod(f = "addPerson", signature = "Context",
definition = function(obj, person) {
obj@persons <- c(obj@persons, person)
return(obj)
}
)
पहले, हम आर-फंक्शन strsplit()
माध्यम से प्रत्येक तत्व की पहचान करने के लिए सीमांकक का उपयोग करके एक सरणी में मूल रेखा को विभाजित करते हैं, फिर प्रत्येक तत्व के लिए दिए गए राज्य के लिए इनपुट मान के रूप में पुनरावृति करते हैं। handle()
विधि फिर से अद्यतन जानकारी ( state
, person
, persons
विशेषता) के साथ context
देता है।
setMethod(f = "parseLine", signature = "Context",
definition = function(obj, s) {
elements <- strsplit(s, ";")[[1]]
# Adding an empty field for considering the end state.
elements <- c(elements, "")
n <- length(elements)
input <- NULL
for (i in (1:n)) {
input <- elements[i]
obj <- handle(obj, input)
}
return(obj@person)
}
)
Becuase R इनपुट तर्क की एक प्रति बनाता है, हमें संदर्भ ( obj
) वापस करने की आवश्यकता है:
setMethod(f = "parseLines", signature = "Context",
definition = function(obj, s) {
n <- length(s)
listOfPersons <- list()
for (i in (1:n)) {
ipersons <- parseLine(obj, s[i])
listOfPersons[[i]] <- ipersons
}
obj@persons <- listOfPersons
return(obj)
}
)
विशेषता persons
S4 Person
वर्ग के उदाहरण की एक सूची है। यह कुछ किसी भी मानक प्रकार के लिए मजबूर नहीं किया जा सकता है क्योंकि आर एक उपयोगकर्ता परिभाषित वर्ग के एक उदाहरण के इलाज के लिए नहीं जानता है। समाधान एक सूची में एक Person
को बदलने के लिए है, पहले से परिभाषित as.list
विधि का उपयोग करके। फिर हम इस फ़ंक्शन को सूची persons
प्रत्येक तत्व पर lapply()
फ़ंक्शन के माध्यम से लागू कर सकते हैं। तब के बगल में मंगलाचरण में lappy()
समारोह, अब लागू होता है data.frame
के प्रत्येक तत्व परिवर्तित करने के लिए समारोह persons.list
एक डेटा फ्रेम में। अंत में, rbind()
फ़ंक्शन को उत्पन्न डेटा फ्रेम की एक नई पंक्ति के रूप में परिवर्तित प्रत्येक तत्व को जोड़ने के लिए कहा जाता है (इसके बारे में अधिक विवरण के लिए इस पोस्ट को देखें)
# Sugestion taken from this post:
# http://stackoverflow.com/questions/4227223/r-list-to-data-frame
setMethod(f = "as.df", signature = "Context",
definition = function(obj) {
persons <- obj@persons
persons.list <- lapply(persons, as.list)
persons.ds <- do.call(rbind, lapply(persons.list, data.frame, stringsAsFactors = FALSE))
return(persons.ds)
}
)
सभी टॉगल कर रहे हैं
अंत में, संपूर्ण समाधान का परीक्षण करने देता है। पार्स करने के लिए लाइनों को परिभाषित करें जहां दूसरी पंक्ति के लिए पता जानकारी गायब है।
s <- c(
"GREGORY BROWN; 25 NE 25TH; +1-786-987-6543",
"DAVID SMITH;786-123-4567",
"ALAN PEREZ; 25 SE 50TH; +1-786-987-5553"
)
अब हम context
प्रारंभिक करते हैं, और लाइनों को पार्स करते हैं:
context <- new("Context")
context <- parseLines(context, s)
अंत में संबंधित डेटासेट प्राप्त करें और उसे प्रिंट करें:
df <- as.df(context)
> df
name address phone
1 GREGORY BROWN 25 NE 25TH +1-786-987-6543
2 DAVID SMITH <NA> 786-123-4567
3 ALAN PEREZ 25 SE 50TH +1-786-987-5553
आइए अब show
हैं show
मेथड्स:
> show(context@persons[[1]])
Person@[name='GREGORY BROWN', address='25 NE 25TH', phone='+1-786-987-6543']
और कुछ उप-राज्य के लिए:
>show(new("PhoneState"))
PhoneState@[name='phone', pattern='^\s*(\+1(-|\s+))*[0-9]{3}(-|\s+)[0-9]{3}(-|\s+)[0-9]{4}$']
अंत में, as.list()
विधि का परीक्षण करें:
> as.list(context@persons[[1]])
$name
[1] "GREGORY BROWN"
$address
[1] "25 NE 25TH"
$phone
[1] "+1-786-987-6543"
>
निष्कर्ष
यह उदाहरण दिखाता है कि OO प्रतिमान का उपयोग करने के लिए R से उपलब्ध तंत्रों में से एक का उपयोग करते हुए, राज्य पैटर्न को कैसे लागू किया जाए। फिर भी, R OO समाधान उपयोगकर्ता के अनुकूल नहीं है और अन्य OOP भाषाओं से बहुत भिन्न है। आपको अपनी मानसिकता को बदलने की आवश्यकता है क्योंकि वाक्य रचना पूरी तरह से अलग है, यह अधिक कार्यात्मक प्रोग्रामिंग प्रतिमान को याद दिलाता है। उदाहरण के बजाय: object.setID("A1")
जैसा कि Java / C # में है, R के लिए आपको इस तरह से विधि को लागू करना होगा: setID(object, "A1")
। इसलिए आपको फ़ंक्शन के संदर्भ प्रदान करने के लिए हमेशा ऑब्जेक्ट को इनपुट तर्क के रूप में शामिल करना होगा। उसी तरह, this
वर्ग की कोई विशेषता नहीं है और या तो एक "."
दिए गए वर्ग के तरीकों या विशेषताओं तक पहुंचने के लिए अंकन। यह अधिक त्रुटि संकेत है क्योंकि एक वर्ग या विधियों को "isState"
विशेषता मान ( "Person"
, "isState"
, आदि) के माध्यम से किया जाता है।
ऊपर कहा गया, एस 4 वर्ग समाधान, सरल कार्यों को करने के लिए पारंपरिक जावा / सी # भाषाओं की तुलना में कोड की अधिक लाइनों की आवश्यकता होती है। वैसे भी, स्टेट पैटर्न इस तरह की समस्याओं के लिए एक अच्छा और सामान्य समाधान है। यह एक विशेष राज्य में तर्क को प्रस्तुत करने की प्रक्रिया को सरल करता है। सभी स्थितियों को नियंत्रित करने के लिए एक बड़ा if-else
ब्लॉक होने के बजाय, हमारे पास प्रत्येक राज्य में बाहर ले जाने के लिए कार्रवाई को कार्यान्वित करने के लिए प्रत्येक State
उप-वर्ग कार्यान्वयन पर छोटे if-else
ब्लॉक हैं।
अटैचमेंट : यहां आप पूरी स्क्रिप्ट डाउनलोड कर सकते हैं।
किसी भी सुझाव का स्वागत है।