A veces un dato de estado necesita compartirse entre varios words sin tener que pasarlo por sus efectos de pila cada vez. Las variables dinámicas de Factor, declaradas con SYMBOL:, le dan a cada word acceso a un valor almacenado en un espacio de nombres.
SYMBOL: y SYMBOLS:
SYMBOL: name declara una variable. SYMBOLS: a b c ; declara varias a la vez:
USING: namespaces ;
SYMBOLS: counter total ;
set-global y get-global (en namespaces) operan sobre el espacio de nombres global:
set-global ( value variable -- )
get-global ( variable -- value )
change-global ( variable quot: ( old -- new ) -- )
0 counter set-global
counter get-global . ! => 0
counter [ 10 + ] change-global
counter get-global . ! => 10
Para el estado a nivel de módulo, las variantes -global son las que necesitas.
with-variable
Una variable dinámica también admite una segunda variante, con ámbito. set / get / change (sin el sufijo -global) leen y escriben el ámbito dinámico actual, y with-variable (en namespaces) crea un ámbito nuevo mientras dura una quotation:
with-variable ( value variable quot -- )
set ( value variable -- )
get ( variable -- value )
change ( variable quot: ( old -- new ) -- )
La variable queda enlazada a value mientras se ejecuta quot; cuando este termina, el enlace desaparece. Esto permite que quien llama desde afuera «inyecte» un valor que el código interno puede leer sin tener que pasarlo por cada firma de pila.
SYMBOL: greeting
"Hello" greeting [
greeting get . ! => "Hello"
"Howdy" greeting set
greeting get . ! => "Howdy"
greeting [ "!" append ] change
greeting get . ! => "Howdy!"
] with-variable
greeting get-global . ! => f (the scoped binding is gone)
Este ejercicio usa la variante global en todo momento, pero reconocer la familia paralela de set/get/change es útil cuando lees código de otras personas.
Como las variables dinámicas guardan una referencia, puedes mantener una colección mutable dentro de una de ellas y actualizarla en el mismo lugar:
SYMBOL: history
V{ } clone history set-global
"first event" history get-global push
"second event" history get-global push
history get-global .
! => V{ "first event" "second event" }
V{ … } es un vector literal. Siempre haz clone de un literal V{ } antes de guardarlo: los literales se comparten entre llamadas.
find: la primera coincidenciafind (en sequences) devuelve el primer elemento para el cual un predicado es verdadero, además de su índice:
find ( seq quot -- index elt/f )
Si nada coincide, ambos valores son f. Descartar el índice con nip y usar unless* es la combinación estándar de «valor por defecto si falta».
reject: descarta las coincidenciasreject (también en sequences) es lo opuesto de filter: devuelve los elementos que no cumplen el predicado.
La asociación de tu comunidad te pide que administres los registros de parcelas del jardín. El estado se guarda en dos variables dinámicas:
registrations — un vector de tuplas plot asignadas actualmente a una persona.next-id — el entero que se usará para el próximo registro.La tupla plot tiene dos campos:
| campo | tipo |
|---|---|
id |
integer |
registered-to |
string |
Define open-garden para inicializar las variables dinámicas: un vector vacío para registrations y 1 para next-id. Después define list-registrations para devolver el vector actual de parcelas.
open-garden
list-registrations .
! => V{ }
Define register para tomar un nombre de la pila, construir una plot nueva con el siguiente id disponible, agregarla al vector registrations, aumentar next-id en uno y devolver la nueva parcela.
open-garden
"Emma Balan" register .
! => T{ plot { id 1 } { registered-to "Emma Balan" } }
list-registrations .
! => V{ T{ plot { id 1 } { registered-to "Emma Balan" } } }
Los ids de las parcelas deben ser únicos y seguir aumentando incluso después de liberar una. next-id nunca debe reutilizar un valor.
Define release para tomar un id y eliminar de registrations la entrada que coincida. Liberar un id desconocido no tiene ningún efecto.
open-garden
"Emma" register drop
1 release
list-registrations .
! => V{ }
Define get-registration para tomar un id y devolver la parcela que coincida, o el símbolo not-found si ninguna parcela tiene ese id.
open-garden
"Emma" register drop
1 get-registration .
! => T{ plot { id 1 } { registered-to "Emma" } }
7 get-registration .
! => not-found
Define find-by-name para tomar un nombre y devolver un vector con todas las parcelas registradas actualmente a nombre de esa persona.
open-garden
"Emma" register drop
"Bob" register drop
"Emma" register drop
"Emma" find-by-name .
! => V{ T{ plot { id 1 } { registered-to "Emma" } }
T{ plot { id 3 } { registered-to "Emma" } } }
Regístrate en Exercism para aprender y dominar Factor con 47 conceptos163 ejercicios y mentoría humana real, todo gratis.