Diseño e implementación de tablas de símbolos en Ruby
-
14-11-2019 - |
Pregunta
Estoy construyendo un analizador léxico en Ruby y estoy a punto de comenzar a recopilar y almacenar símbolos en la tabla de símbolos.Mi pregunta principal es sobre el diseño del símbolo y si debe ser una tabla estática (lo que significa que todos los datos se mantendrán en el nivel de clase) o si debe ser de una instancia a otra.
Opción 1:Estructura de datos a nivel de clase
require 'SymbolTableEntry.rb'
class SymbolTable
@sym_table = Array.new(500)
def initialize()
end
def SymbolTable.add(element, index)
@sym_table[index] = element if element.is_a? SymbolTableEntry
end
def SymbolTable.to_s
pp @sym_table
end
end
Con este esquema, la clase SymbolTable tiene una especie de funcionalidad "estática", lo que significa que en realidad no creo una instancia de SymbolTable, el único objeto que existe es el de nivel de clase.
(Asumir que SymbolTableEntry
es un objeto válido aunque no lo defina aquí)
Ex:
irb(main):002:0> require 'SymbolTable.rb'
=> true
irb(main):003:0> ste = SymbolTableEntry.new
=> #<SymbolTableEntry:0x7ef36884>
irb(main):004:0> SymbolTable.add(ste, 10)
=> #<SymbolTableEntry:0x7ef36884>
irb(main):005:0> SymbolTable.to_s
[nil,
nil,
nil,
nil,
nil,
nil,
nil,
nil,
nil,
nil,
#<SymbolTableEntry:0x7ef36884>]
=> nil
Opcion 2:Estructura de datos a nivel de instancia
require 'rubygems'
require 'backports'
require 'SymbolTableEntry.rb'
class SymbolTable
def initialize()
@sym_table = Array.new(10)
end
def add(element, index)
@sym_table[index] = element if element.is_a? SymbolTableEntry
end
def to_s
pp @sym_table
end
end
Con este esquema, realmente necesitaría crear una instancia de la clase SymbolTable para poder agregar valores a la tabla de símbolos.
irb(main):001:0> require 'SymbolTable.rb'
=> true
irb(main):002:0> st = SymbolTable.new
=> #<SymbolTable:0x7eeb6c9c @sym_table=[nil, nil, nil, nil, nil,
nil, nil, nil, nil, nil]>
irb(main):003:0> ste=SymbolTableEntry.new
=> #<SymbolTableEntry:0x7eeb4d5c>
irb(main):004:0> st.add(ste,10)
=> #<SymbolTableEntry:0x7eeb4d5c>
irb(main):007:0> st.to_s
[nil,
nil,
nil,
nil,
nil,
nil,
nil,
nil,
nil,
nil,
#<SymbolTableEntry:0x7eeb4d5c>]
=> nil
Me encantaría escuchar todos los comentarios sobre qué diseño usaría o preferiría usar, así como cualquier comentario sobre el código Ruby en general.
Gracias
Solución
Prefiera las variables de instancia a las variables de clase, aunque solo sea porque las clases que usan variables de clase son un poco más complicadas para las pruebas unitarias.
Puede usar variables de instancia y aún tener una tabla de símbolos para gobernarlos a todos.Una forma es asignando su tabla de símbolos a una variable global:
$symbol_table = SymbolTable.new
En algunos idiomas, las clases que utilizan variables globales son difíciles de probar.En Ruby, no son tan malos, ya que el tipeo pato te permite asignar objetos simulados a variables globales antes de ejecutar el objeto bajo prueba.
O puede utilizar el patrón singleton.Ruby viene con una biblioteca para facilitar esto:
require 'singleton'
class SymbolTable
include Singleton
...
end
Para recuperar la única instancia de SymbolTable, créela si es necesario:
SymbolTable.instance
Otros consejos
Utilice variables de instancia.Pero no (al menos no sólo) por razones de manejo de pruebas.Más bien porque
- Cada proceso de análisis producirá su propia tabla de símbolos, por lo que es posible que tenga más de una a la vez.
- la tabla de símbolos solo es necesaria mientras el proceso de análisis esté en marcha
- las variables de clase introducen la necesidad de realizar sincronización para lograr la seguridad de los subprocesos, aunque cada proceso de análisis puede vivir perfectamente con su propio conjunto de símbolos.
Salud
roberto
Solo una rápida aclaración a un par de las respuestas dadas por Robert y Wayne, las cuales mencionan variables de clase.
La pregunta original no propone utilizar variables de clase en absoluto, pero sí pregunta sobre el uso variables de instancia de clase.La primera opción que presentó Hunter utilizó el objeto de clase en sí como la única instancia de la tabla de símbolos (con el estado almacenado en las variables de instancia de clase), mientras que la segunda opción usó un marco de clase/instancia más típico con el estado almacenado en una instancia del símbolo. mesa.
Las variables de clase de Ruby no son lo mismo que las variables de instancia de clase y, en general, deben evitarse.