domingo, 31 de marzo de 2024

Procedimientos. Textos.

Enumeración

Inicio con esta entrada una nueva sección dentro del apartado o capítulo que he denominado Usos y que tiene que ver con la escritura de unos textos que requieren un tratamiento especial: me refiero a las enumeraciones y los listados. Por simplificar la estructura de contenidos de Usos he considerado conveniente incluir en esta sección también el trabajo con tablas, ya que uno de los usos de las tablas (aunque más orientado a la presentación y estructuración de los contenidos en el texto), también es una de las formas en las que podemos escribir listados.


En el proceso de escritura de textos nos encontramos, con cierta frecuencia, con enumeraciones y listados. Aunque en la escritura manual ambos no presentan mayor complicación (1),  es en la automatización de la composición del texto cuando ambas presentan especificidades, no siendo suficiente con los recursos de escritura que ya vimos anteriormente en este mismo apartado (Usos). En esta entrada trataré sobre la composición de enumeraciones y en siguientes sobre listas y tablas.

La escritura manual de una enumeración (lunes, martes, miércoles y jueveses muy simple: escribimos cada uno de los elementos que la componen, separándolos por comas y usando bien la conjunción, bien la disyunción entre el penúltimo y el último elemento de la enumeración. ¿Pero cómo reproducir este proceso en la automatización del texto?.

El problema de la enumeración (aunque podemos considerarlo meramente de corrección de estilo y gramatical) está en el tratamiento de los conectores (coma y/o disyunción vs. conjunción). La causa de las "dificultades" está en el conocimiento vs. desconocimiento previo del número de elementos que componen la enumeración. Otras cuestiones a considerar en la creación del código son si la entrada de datos queda a disposición del usuario o se dan por supuestas y si nos vamos a basar en la concatenación de contenidos asociados a una (o varias) variable(s) o en el uso de matrices... o en la combinación de ambas soluciones.

El "caso" más simple es cuando los elementos vienen dados en el código y, además, conocemos de antemano tanto el número de elementos como su contenido concreto; en este caso tenemos información cierta de los contenidos (y no necesitamos solicitarlos al usuario) y sabemos qué conector utilizar en cada momento. Esta situación (2) podemos tratarla tanto mediante variable(s) concatenadas incrementales, como mediante el listado de los elementos de la matriz.

Supongamos que se trata de enumerar, por ejemplo, los días de la semana (3), datos conocidos de antemano y perfectamente tratables como variables o como elementos de una matriz, como parte de un texto que, por simplicidad, vamos a obviar.

Esta enumeración podemos abordarla, como dije, mediante mediante variables (a las que asignar los nombres de los días, o mediante una matriz. Dado que el primer sistema hace que el código sea más simple pero también más costoso de escribir, opto por el uso de una matriz. Además, en este caso, lo importante no es tanto el soporte de los datos (variables vs. matriz) como el sistema de  concatenación, que es lo que me interesa explicar en primer lugar. Veamos este  script básico:

Sub Main1

VarSis

Dim mDiasSem(6) As String
Dim i As Integer

 

mDiasSem(0) = "lunes"
mDiasSem(1) = "martes"
mDiasSem(2) = "miércoles"
mDiasSem(3) = "jueves"
mDiasSem(4) = "viernes"
mDiasSem(5) = "sábado"
mDiasSem(6) = "domingo"


For i = LBound(mDiasSem()) To UBound(mDiasSem()):
Escribir(mDiasSem(i))
If i < UBound(mDiasSem()) - 1 Then
Escribir(", ")
ElseIf i = UBound(mDiasSem()) - 1 Then
Escribir(" y ")
End If
Next

End Sub

Se pueden identificar tres parte en el script (4): en la primera se declara la matriz mDiasSem() y una variable contador (i); en la segunda se dota de contenido a la matriz y en la tercera se desarrolla el procedimiento de escritura de la enumeración. Estés última la que nos interesa ahora.

Consta ésta de un ciclo For articulado en torno a la identificación de las posición o índices inicial (función LBound()) y final (función UBound()) de la matriz, que se asocian al contador i. El uso de estas funciones en lugar de los valores directos (0 y 6 respectivamente) facilita que esta línea de código sea válida para cualquier matriz con independencia del número de elementos que tenga.

Dentro del bucle incluimos la instrucción de escritura Escribir(mDiasSem(i)) y un condicional: la primera escribe en el documentos los contenidos de la matriz y la segunda (el condicional if), genera la concatenación y establece la forma en que se concreta gramaticalmente: la coma (,) hasta que el penúltimo elemento... 

If i < UBound(mDiasSem()) - 1 Then

... y la conjunción entre éste y el último

ElseIf i = UBound(mDiasSem()) - 1 Then

La concatenación resultante lo es por la acción sucesiva (cíclica) de la llamada a la subrutina de escritura, la cual podemos simular ahora  como sigue, entendiendo que cada elemento va precedido de la llamada a la subrutina Escribir() (5)...

 mDiasSem(i) + ", ")+ mDiasSem(i) +.... + " y " + mDiasSem(i)

... siendo éste el resultado que obtendremos:

 lunes, martes, miércoles, jueves, viernes, sábado y domingo

La segunda situación se presenta cuando desconocemos a priori el número de elementos que contiene la enumeración ni su contenido concreto y hacemos la enumeración usando variables (6). En este caso el script (uno de los posibles) sería el siguiente:

Sub Main2

VarSis

Dim DiasSem As String, Conector As String, Dias As String
Dim i As Integer, a As Integer

a = CInt(InputBox("Número de elementos de la lista"))
a = a - 1

For i = 0  To a:
DiasSem =InputBox("Día de la semana")
If i < 1 Then
Dias = DiasSem
ElseIf i = a Then
Conector = " y "
Dias = Dias & Conector & DiasSem
Else
Dias = Dias & ", " & DiasSem
End If
Next

Escribir(Dias)

End Sub

En este caso distinguimos cuatro partes dentro del script:

  • En la primera establecemos las variables: tres string y dos integer. Las segundas son contadores y dentro de las primeras, dos de ellas (DiasSem y Dias) sirven para concretar el contenido de la enumeración y la tercera (Conector) sirve para especificar los conectores a emplear.
  • En la segunda parte establecemos el número de elementos que tendrá la enumeración. En este caso lo hemos hecho mediante un recurso elemental de interface (InputBox())para que esté en función de la elección del usuario (7). Debido a esto, para evitar la confusión que podría darse entre número de elementos de la enumeración y el elemento último del ciclo (ya que el primer valor del contador suele ser por sintaxis 0 y no 1), he realizado una reasignación del contenido de contador secundario (a = a-1).
  • La tercera parte es similar en cuanto a estructuras a la de Main1, pero muy diferente en cuanto a las instrucciones que contiene. La explicaré en detalle al final de estos epígrafes.
  • Y la última y final es la de escritura de la enumeración en el documento, haciendo uso de la subrutina Escribir(Dias). Nótese que, en este caso, la instrucción de escritura no es cíclica (no está dentro de For) ya que la concatenación, que sí se desarrolla dentro del bucle, da como resultado una variable generada mediante un proceso de asignación de contenido que incluye el contenido previo de esa misma variable. Explico todo esto a continuación.
El ciclo For tiene como límite el que defina el usuario en a (2º parte), o que equivale a decir el número de días que va a seleccionar, no cuáles. Para esto, también mediante InputBox(), la instrucción (DiasSem =InputBox("Día de la semana")) le permite introducir los valores que desee hasta alcanzar su total a. En DiasSem, por tanto, se almacenan los valores individuales.

A continuación, mediante la estructura condicional If-ElseIf-Else creamos el contenido que resultará en concatenación sobre la variable Dias, que es la que se emplea como argumento para la llamada a la subrutina Escribir (parte 4ª). La variable Dias es, pues, el soporte en el que realizamos el proceso de concatenación de la secuencia, y sobre la cual debemos articular el ajuste del contenido de la variable Conector. Esta es la razón de la estructura condicional: inicialmente (para i = 0, o lo que es lo mismo, para i<1) Dias contiene el valor de DiasSem, pero en los pasos siguientes del ciclo, entre en funcionamiento la concatenación que incluye la variable Concatenar (8). Si llegamos al final de la enumeración el conector será "y", pero en otro caso empleamos como conector la coma (,).

Gracias a las posibilidades de interacción del código con el usuario, mediante este script podremos incluir en la enumeración tanto elementos como deseemos, además de los elementos que deseemos. No estamos limitados a una lista previa (a un marco de referencia, en realidad), como tampoco a un número prefijado de componentes, como sí sucedía en el primer script.

Veamos, por último, un tercer procedimiento que corresponde también con una tercera situación, menos restrictiva que la primera, pero más que la segunda: disponemos de todos los datos posibles (universo) para la enumeración y seleccionamos de entre ellos los que nos resultan útiles (conjunto). Se trata de una situación que quizá se da con más frecuente en la elaboración de listados que en enumeraciones (donde puede ser más frecuente el modelo anterior), pero también sirve, muy especialmente, cuando nos interesa y conviene controlar el contenido que tendrá como referencia (universo) la enumeración o el listado. Especialmente si ese universo del que extraer el conjunto es extenso y/o difícil de recordar de memoria (para el usuario).

Sub Main3

VarSis

Dim mDiasSem(6) As String, mMisDias()As String
Dim Dia As Integer
Dim i As Integer, a As Integer

mDiasSem(0) = "lunes"
mDiasSem(1) = "martes"
mDiasSem(2) = "miércoles"
mDiasSem(3) = "jueves"
mDiasSem(4) = "viernes"
mDiasSem(5) = "sábado"
mDiasSem(6) = "domingo"

a = 0

For i = LBound(mDiasSem()) To UBound(mDiasSem())
Dia = MsgBox (mDiasSem(i),4,"MIS DIAS")
If Dia = 6 Then
ReDim Preserve mMisDias(a)
mMisDias(a) = mDiasSem(i)
a=a+1
End If
Next

For i = LBound(mMisDias()) To UBound(mMisDias()):
If i < UBound(mMisDias()) - 1 Then
Escribir(", ")
ElseIf i = UBound(mMisDias()) - 1 Then
Escribir(" y ")
End If
Next

Escribir(mMisDias(i))

End Sub

Este script consta también de cuatro partes: la declaración de variables y matrices, la asignación de contenido a la matriz universo, un bucle para construir la matriz conjunto y un segundo bucle para escribir el contenido de ese conjunto en el documento (esto es: para crear la enumeración, o el listado en su caso). Veamos con más detalle cada una de estas partes:

  • En la primera parte se declaran las matrices y variables. Las dos matrices cumplen funciones de universo (mDiasSem(6)) y conjunto (mMisDias()): en la primera se concreta el tamaño ya que es conocido, pero no en la segunda por no serlo. Dentro de las variables se distinguen dos grupos: Dia, que servirá para contener el valor numérico de la respuesta de elección del usuario y dos variables contador (i y a) para el funcionamiento de los bucles.
  • En la segunda parte se asigna contenido a la matriz universo (mDiasSem()) para que esté disponible para el proceso de selección de contenidos que pasarán a la matriz conjunto (mMisDias())
  • La tercera parte se inicia col la asignación de valor inicial al contador a (a=0), aunque lo fundamental en ella es el bucle For cuya estructura explicaré al final de esta descripción. Este primer bucle tiene por misión facilitar la elección de los contenidos que conformarán la matriz conjunto (mMisDias())
  • Finaliza el script (cuarta parte) con un segundo bucle For, cuya función es facilitar la escritura del contenido seleccionado (matriz mMisDias()) en el documento, por lo que en él se hace uso de la subrutina de escritura (Escribir). También explicaré más detalladamente el funcionamiento de este bucle.
Paso a detallar cómo funciona el bucle de la parte tercera:
  • Utilizo las funciones LBound() y UBound() sobre la matriz mDiasSem() (conjunto) asociadas al contador i a fin de facilitar la generalización del funcionamiento del bucle. Dado que las dimensiones de la matriz conjunto son conocidas, podría sustituir esas funciones por los valores directos (0 - 6), pero es más funcional utilizar los valores resultantes de esas funciones, ya que así se ajusta el bucle a las dimensiones de cualquier matriz.
  • La instrucción que sigue (Dia = MsgBox (mDiasSem(i),4,"MIS DIAS")) permite que el usuario selecciones los valores de la matriz conjunto al incluirla como primer parámetro de la función MsgBox(), la cual se utiliza aquí como función (y no como sistema de información al usuario), ya que lo que importa es el valor (6 vs. 7) que resulta de la pulsación de los botones (-No) que contiene dicha función como segundo parámetro (4) (9)
  • En función de la elección (vs. No) se obtiene un valor (6 - 7) que sirve como argumento para que se cumpla o no la condición que se establece en el condicional If que sigue tras la elección (If Dia = 6 Then). El valor de referencia (6 <- ), que indica que se cumple la condición exigida (supone que se ha seleccionado el elemento de la matriz conjunto) permite que se desarrolle el proceso subsiguiente.
  • Este proceso implica tres pasos:
    • Redimensionar la matriz conjunto manteniendo su contenido previo (ReDim Preserve mMisDias(a)) en  función al valor del contador a en ese momento del ciclo.
    • Pasar el valor seleccionado (mDiasSem(i)) como elemento de la matriz conjunto (mMisDias(a) = mDiasSem(i))
    • Asignar un nuevo valor a a (a=a+1) para incrementar en el siguiente ciclo la dimensión de mMisDias(a).
De este modo es el usuario el que selecciona los valores que necesita y la matriz conjunto resultante de estas elecciones queda dimensionada al número de elecciones positivas del usuario, esto es: a su contenido real.

El segundo bucle For (parte cuarta) tiene como objetivo la correcta escritura de la enumeración, siendo ésta concatenación de elementos resultante del propio funcionamiento del bucle. De que la gramática sea correcta (10) se encarga el condicional anidado en el bucle: 
  • Si el valor del contador i en ese momento es inferior al límite superior de la matriz (If i < UBound(mMisDias()) - 1), entonces se pasa como parámetro la coma (",") en la llamada a la subrutina de escritura (Escribir(", "))
  • Pero si se alcanza la posición inmediatamente anterior al límite superior de la matriz (ElseIf i = UBound(mMisDias()) - 1), entonces se pasa como parámetro la conjunción ("y") (Escribir(" y "))
En esta entrada hemos visto cómo condicionar un contenido concreto (los conectores gramaticales de una enumeración) a una determinada posición en la secuencia de componentes de una matriz o de la variable resultante de la concatenación de string mediante el uso de estructuras condicionales. También hemos utilizado bucles como herramienta de selección de contenidos y de escritura secuencial de los mismos y hemos trabajado con matrices como origen (universo) y destino (conjunto) de datos. Mediante estos procedimientos hemos construido enumeraciones que se ajustan a las demandas del usuario y a la gramática en su escritura como texto.

Este es el documento que contiene los diferentes script principales y auxiliares.

NOTAS

(1) Las enumeraciones forman parte del proceso básico de escritura del texto y para crear listas disponemos de recursos específicos que aporta el servicio (Writer, en nuestro caso).

(2) Se trata de una "situación" que se puede presentar muy raramente, pero que es necesario contemplarla en esta entrada por coherencia en el proceso de aprendizaje.

(3) Por motivos didácticos, mantendré el mismo ejemplo en el resto de los "casos".

(4) Por simplicidad, y por ser muy conocido, no me detendré a explicar la llamada a VarSis ni incluyo tampoco la macro-subrutina de escritura Escribir() necesaria, no obstante, para el funcionamiento de Main1.

(5) Podemos sustituir el concatenador + por & (ambos funcionales) pero prefiero usar + para diferenciar este procedimiento del que resulta de la asignación de una variable a si misma. En eso consiste la segunda forma de concatenar los contenidos.

(6) Tomar como ejemplo los días de la semana resulta un poco forzado en este contexto, ya que sí sabemos de antemano el universo de contenidos de ese conjunto, pero mantenemos esa referencia para facilitar la comprensión del procedimiento y de sus diferencias con el anterior. Al igual que en el anterior omito explicaciones sobre VarSis y la subrutina de escritura, que es la misma.

(7) Podría haber optado por asignarlo directamente, lo cual sería válido a nivel didáctico, pero el script resultante perdería funcionalidad práctica. El inicio de bucle en i=0 lo es por respetar la sintaxis normal de la instrucción For (For i = 0 To a:)

(8) Ahora Dias resulta de añadir a Dias una secuencia formada por el conector + el contenido de la variable DiasSem en cada vuelta del bucle: Dias = Dias & Conector & DiasSem. Obsérvese que ahora usamos el signo &, que es el apropiado a un cadena resultante de concatenar string.

(9) Estos valores (6 para Sí, 7 para No) están predeterminados por la sintaxis de la función MsgBox() según el lenguaje OOo Basic.

(10) Me refiero aquí al uso del los conectores adecuados ( "," vs. "y") según la posición en la enumeración.

No hay comentarios:

Publicar un comentario

Comenta esta entrada