Menu
CoddyTech

Cierres como objetos

Parte de la sección Programación orientada a objetos del Journey de R de Coddy. Lección 17 de 57.

Ya conociste las clausuras en Lógica y flujo: una función creada dentro de otra función conserva las variables que la rodean. Una fábrica de funciones que devuelve una lista de esas funciones crea un objeto. Sus variables locales son los datos del objeto, y las funciones son sus métodos:

make_account <- function(balance = 0) {
  deposit <- function(x) balance <<- balance + x
  get_balance <- function() balance
  list(deposit = deposit, get_balance = get_balance)
}
acc <- make_account(100)
acc$deposit(50)
print(acc$get_balance())

Salida:

[1] 150

Cada llamada a la fábrica vuelve a ejecutar su cuerpo, así que cada objeto obtiene sus propias variables. Cambiar una cuenta no afecta a la otra:

make_account <- function(balance = 0) {
  list(deposit = function(x) balance <<- balance + x,
       get_balance = function() balance)
}
ada <- make_account(100)
bo <- make_account(5)
ada$deposit(1)
cat(ada$get_balance(), bo$get_balance())

Salida:

101 5

Los datos son privados. balance no es un elemento de la lista, así que acc$balance es NULL y asignarle un valor solo añade un elemento no relacionado. Las funciones de la lista constituyen toda la interfaz pública del objeto:

make_account <- function(balance = 0) {
  list(get_balance = function() balance)
}
acc <- make_account(100)
print(acc$balance)
acc$balance <- 1e6
print(acc$get_balance())

Salida:

NULL
[1] 100

Los métodos pueden llamarse entre sí cuando se definen como funciones locales antes de crear la lista. Aquí, add_many() reutiliza add():

make_tally <- function() {
  total <- 0
  add <- function(x) total <<- total + x
  add_many <- function(xs) for (x in xs) add(x)
  get <- function() total
  list(add = add, add_many = add_many, get = get)
}
t <- make_tally()
t$add(1)
t$add_many(c(2, 3, 4))
print(t$get())

Salida:

[1] 10
challenge icon

Desafío

Fácil

Completa la función fábrica make_counter(start = 0) en Counter.R. Mantiene privado el valor actual y devuelve una lista con cuatro funciones: increment() y decrement() cambian el valor en 1, reset() lo restablece a start y value() lo devuelve.

El código proporcionado lee el valor inicial y, después, los comandos +, - o reset, e imprime el valor después de cada uno. Al final imprime los nombres de la lista devuelta, que deben ser únicamente los de las cuatro funciones.

Tu código va en Counter.R. main.R contiene el código de entrada/salida proporcionado y no se puede editar.

Pruébalo tú mismo

source("Counter.R")

# Código de entrada/salida proporcionado: déjalo tal como está
input <- suppressWarnings(readLines(file("stdin")))
counter <- make_counter(as.numeric(input[1]))
for (cmd in input[-1]) {
  if (cmd == "+") counter$increment()
  if (cmd == "-") counter$decrement()
  if (cmd == "reset") counter$reset()
  cat(cmd, " -> ", counter$value(), "\n", sep = "")
}
cat("fields visible: ", paste(sort(names(counter)), collapse = ", "), "\n", sep = "")
quiz iconPonte a prueba

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

Practica por tu cuenta: Compilador de R online