A veces, un fragmento de estado debe compartirse entre varias palabras sin tener que pasarlo cada vez por sus efectos de pila. Las variables dinámicas de Factor, declaradas con SYMBOL:, dan a cada palabra 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 scope. set / get / change (sin el sufijo -global) leen y escriben el scope dinámico actual, y with-variable (en namespaces) crea un scope nuevo mientras dura una quotation:
with-variable ( value variable quot -- )
set ( value variable -- )
get ( variable -- value )
change ( variable quot: ( old -- new ) -- )
La variable queda vinculada a value mientras se ejecuta quot; en cuanto este termina, la vinculación desaparece. Esto permite que quien llama desde fuera «inyecte» un valor que el código interno puede leer sin 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 set/get/change resulta útil cuando lees el código de otras personas.
Como las variables dinámicas mantienen una referencia, puedes guardar una colección mutable dentro de una y actualizarla in situ:
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. Haz siempre 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 que un predicado se evalúa como verdadero, junto con 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: descartar las coincidenciasreject (también en sequences) es lo contrario de filter: devuelve los elementos que no cumplen el predicado.
Tu asociación vecinal te pide que gestiones las inscripciones 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 debe usar para la siguiente inscripción.La tupla plot tiene dos ranuras:
| ranura | 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 de parcelas actual.
open-garden
list-registrations .
! => V{ }
Define register para tomar un nombre de la pila, construir una plot
nueva con el siguiente id disponible, añadirla al vector registrations,
incrementar next-id en uno y devolver la nueva plot.
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 id de las parcelas deben ser únicos y aumentar incluso después de
liberar una parcela: next-id nunca debe reutilizar un valor.
Define release para tomar un id y eliminar de registrations la entrada
correspondiente. 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
correspondiente, 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.