setValidity y validObject
Parte de la sección Programación orientada a objetos del Journey de R de Coddy. Lección 30 de 57.
setValidity("Class", function(object) ...) añade la regla de validez después de declarar la clase, lo que mantiene corta una llamada larga a setClass(). Al igual que setGeneric(), devuelve un valor que se imprime en el nivel superior, así que envuélvelo en invisible():
setClass("Account", slots = c(owner = "character", balance = "numeric"))
invisible(setValidity("Account", function(object) {
if (object@balance < 0) return("balance must not be negative")
TRUE
}))
msg <- tryCatch(new("Account", owner = "Ada", balance = -1), error = function(e) conditionMessage(e))
cat(msg)Salida:
invalid class “Account” object: balance must not be negativeLos cambios deben realizarse mediante métodos que comprueben el resultado. Un método withdraw() cambia el slot, llama a validObject() y devuelve el objeto solo cuando sigue siendo válido:
setClass("Account", slots = c(balance = "numeric"))
invisible(setValidity("Account", function(object) {
if (object@balance < 0) "balance must not be negative" else TRUE
}))
invisible(setGeneric("withdraw", function(acc, amount) standardGeneric("withdraw")))
setMethod("withdraw", "Account", function(acc, amount) {
acc@balance <- acc@balance - amount
validObject(acc)
acc
})
a <- new("Account", balance = 50)
a <- withdraw(a, 20)
msg <- tryCatch({ a <- withdraw(a, 100); "done" }, error = function(e) "refused")
cat(msg, a@balance)Salida:
refused 30Un par de accesores puede estar compuesto por genéricos: balance(x) lee y un genérico de reemplazo "balance<-" escribe. Su método comprueba el objeto, por lo que balance(a) <- -5 se rechaza como cualquier otro cambio no válido:
setClass("Account", slots = c(balance = "numeric"),
validity = function(object) if (object@balance < 0) "balance must not be negative" else TRUE)
invisible(setGeneric("balance", function(x) standardGeneric("balance")))
invisible(setGeneric("balance<-", function(x, value) standardGeneric("balance<-")))
setMethod("balance", "Account", function(x) x@balance)
setMethod("balance<-", "Account", function(x, value) {
x@balance <- value
validObject(x)
x
})
a <- new("Account", balance = 10)
balance(a) <- 25
msg <- tryCatch({ balance(a) <- -5; "changed" }, error = function(e) "refused")
cat(balance(a), msg)Salida:
25 refusedvalidObject(x, test = TRUE) no se detiene. Devuelve TRUE para un objeto válido o el texto del problema, lo cual resulta práctico para un informe:
setClass("Box", slots = c(w = "numeric"),
validity = function(object) if (object@w <= 0) "w must be positive" else TRUE)
b <- new("Box", w = 2)
print(validObject(b, test = TRUE))
b@w <- 0
print(validObject(b, test = TRUE))Salida:
[1] TRUE
[1] "w must be positive"Desafío
FácilLa clase Account en Account.R tiene las ranuras owner y balance. Completa el archivo:
- añade una regla con
setValidity()(envuelta eninvisible()) que devuelvaowner must not be emptyobalance must not be negative; - crea los genéricos
deposit(acc, amount)ywithdraw(acc, amount)y sus métodos deAccount:depositdebe detenerse conamount must be positive(usacall. = FALSE) para cantidades de 0 o menos; ambos deben cambiar el saldo, llamar avalidObject()y devolver la cuenta; - añade un método
show()que imprimaada: 50y una nueva línea.
El código proporcionado lee el propietario, después las líneas dep 50 o wd 20, e imprime la cuenta después de cada cambio o refused: y el problema.
Tu código va en Account.R. main.R contiene el código proporcionado de entrada/salida y no se puede editar.
Pruébalo tú mismo
source("Account.R")
# Código de entrada/salida suministrado: déjalo tal como está
input <- suppressWarnings(readLines(file("stdin")))
acc <- tryCatch(new("Account", owner = input[1], balance = 0),
error = function(e) { cat("error: ", sub(".*object: ", "", conditionMessage(e)), "\n", sep = ""); NULL })
if (!is.null(acc)) {
for (line in input[-1]) {
p <- strsplit(line, " ")[[1]]
amount <- as.numeric(p[2])
acc <- tryCatch({
changed <- if (p[1] == "dep") deposit(acc, amount) else withdraw(acc, amount)
print(changed)
changed
}, error = function(e) {
cat("refused: ", sub(".*object: ", "", conditionMessage(e)), "\n", sep = "")
acc
})
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación orientada a objetos
1Fundamentos de S3
Trabajar con archivosListas con una claseFunciones constructorasMétodos de impresión y formatoValidadores y auxiliaresRepaso: temperaturas4Encapsulación en R
Cierres como objetosEntornos como objetosFunciones de accesoProtección del estadoRepaso: parquímetro2S3 Genéricos y métodos
Genéricos y UseMethodMétodos predeterminadosMétodos para genéricos baseFundamentos de NextMethodRepaso: áreas de figuras5Clases S4
Definición de clases S4Slots y accesoComprobaciones de validezMétodos showRepaso - Catálogo de productos8Clases de referencia
Definición de clases de referenciaMétodos y actualizaciones de camposSemántica de copiaHerencia y callSuperRepaso: cola de tareas11Proyecto: Gestión de biblioteca
Libros y miembrosPréstamo de libros3Herencia en S3
Vectores de claseOrden de búsqueda de métodosExtensión con NextMethodComprobación con inherits()Repaso - Nómina del personal6S4 Genéricos y métodos
Creación de genéricosMétodos para funciones incorporadasDespacho sobre dos argumentossetValidity y validObjectRepaso: dinero y monedaPractica por tu cuenta: Compilador de R online