Mostrando entradas con la etiqueta Matrices. Mostrar todas las entradas
Mostrando entradas con la etiqueta Matrices. Mostrar todas las entradas

domingo, 10 de agosto de 2025

Textos. OOo Basic

Cambio de palabras mediante matrices


Hay ocasiones en las que nos interesa cambiar unas palabras por otras en un texto de forma automática; un ejemplo puede ser cuando deseamos expresar en femenino lo que está en masculino (o viceversa) o en plural lo que está en singular (y viceversa). Para ello podemos aplicar procedimientos basados en condicionales, pero éstos presentan limitaciones en cuanto a generalización (cuando la condicionalidad va asociada a la aplicación de reglas) o debido a un alto coste de codificación y procesamiento. La alternativa del uso de matrices es una alternativa interesante, como veremos en esta entrada.


Pondré un ejemplo relativamente sencillo y concreto en el que esta estrategia se usa para cambiar palabras de masculino a femenino, después de haber segmentado la frase original para obtener una matriz con las palabras que la componen.

Sub CambioPalMatiz

Dim sFraseOr As String
Dim mMatrizOr() As String
Dim mMasc() As String, mFem() As String
Dim sFraseFem As String
Dim i As Integer, ii As Integer

'Frase original
sFraseOr = "el alumno estudia matemáticas"

'Matriz de palabras en masculino
mMasc() = Array("el","un","al","del","niño","alumno")

'Matriz de palabras en femenino
mFem() = Array("la","una","a la","de la","niña","alumna")

'Segmento la frase original en las palabras que la componen
mMatrizOr() = Split(sFraseOr)

'Comparo el contenido sobre la matriz mMasc()
For i = 0 To UBound(mMatrizOr())
For ii = 0 To UBound(mMasc())
If mMatrizOr(i) = mMasc(ii) Then
mMatrizOr(i) = mFem(ii)
Exit For
End If
Next
Next

'Reconstruyo la frase ahora en femenino y la muestro mediante MsgBox()
sFraseFem = Join (mMatrizOr())
MsgBox (sFraseFem)

End Sub

En esta propuesta es importante que las matrices mMasc() y mFem() estén ajustadas a las frases que se dan como entradas, cosa que se puede hacer de varias maneras, aunque aquí la que se propone es la más simple: incrementar el listado de los arrays (mMasc() = Array("el","un","al","del","niño","alumno")).

Una vez contamos con las matrices necesarias (incluyendo la construcción de la derivada de la frase de entrada mediante la función Split() (mMatrizOr() = Split(sFraseOr)), es la estructura de bucles For anidados la responsable de ir comparando los elementos de esa matriz derivada con los elementos de la matriz a comparar (mMas() en este caso, pero no necesariamente) mediante un condicional (If mMatrizOr(i) = mMasc(ii) Then), devolviendo, en su caso, el término equivalente en femenino, reasignado a la posición del elemento de la matriz derivada (mMatrizOr(i) = mFem(ii)).

Finalizamos el procedimiento volviendo a presentar como frase los componentes de la matriz derivada de la original, que ahora presenta las palabras masculinas sustituidas por sus equivalentes en femenino (sFraseFem = Join (mMatrizOr()))

martes, 3 de junio de 2025

Textos. OOo Basic

Matrices como recurso para generar datos (output)


También podemos utilizar matrices asociadas a bucles como recurso para generar datos de salida. En realidad no es un procedimiento muy diferente al que empleamos para [facilitar el input] como podremos ver en esta entrada.



En este caso tenemos que dar por supuesto que la matriz de datos cuenta con todos los necesarios, sin entrar en detalles sobre el modo en que llegamos a esta situación (1). Suponemos también que nos enfrentamos a la tarea de automatizar la generación del texto de salida (output) (2) siendo el script el que sigue:

Dim mDatos(7) As String, vTexto As String
Dim i As Integer

mDatos(0) = "Con fecha"
mDatos(1) = "12 de mayo de 2024"
mDatos(2) = "en el CP Caudalia"
mDatos(3) = "tiene lugar la reunión de tutoría"
mDatos(4) = "con la madre"
mDatos(5) = "del alumno"
mDatos(6) = "Carlos López"

Para reconfigurar los segmentos textuales contenidos en la matriz en un único string (3), usamos el bucle For como sigue

vTexto = ""

For i = 0 To UBound(mDatos())
vTexto = vTexto & " " & mDatos(i)
Next

MsgBox (vTexto)

Quede aclarado que MsgBox() suple aquí el procedimiento de escritura del texto resultante en el documento (output), simplificación que me permito por motivos didácticos. Pero lo importante es explicar el funcionamiento del bucle.
  • Recorremos la matriz mDatos() desde su inicio (i = 0) hasta su final (UBound(mDatos()) mediante el bucle For (For i = 0 To UBound(mDatos()))...
  • Asignando a la variable vTexto el contenido de cada uno de los elementos de la matriz (vTexto = vTexto & " " & mDatos(i))...
  • Instrucción en la que quiero incidir en dos detalles:
    • La recursividad: en el nuevo ciclo, sobre la variable vTexto escribimos el contenido de la propia variable en el ciclo precedente (vTexto = vTexto & ...)
    • El añadido de una cadena vacía como separador entre segmentos (& " " &), que no sería necesaria si cada segmento ya la incluyera como elemento final de su string, solución que no recomiendo.
El resultado presenta una pequeña "deficiencia": el string resultante se inicia con un espacio en blanco debido al modo en que se construye la secuencia. Para la mayoría de los usos del procedimiento esto puede resultar irrelevante, así que la solución anterior puede ser más que suficiente. No obstante si nos interesa eliminar ese espacio al inicio de la cadena podremos utilizar la alternativa que sigue...

For i = 0 To UBound(mDatos())
If i = 0 Then
vTexto = mDatos(i)
Else
vTexto = vTexto & " " & mDatos(i)
End If
Next

... en la que mediante un condicional diferenciamos el procedimiento al inicio del ciclo (If i = 0 Then -> vTexto = mDatos(i)) en el que asignamos directamente a vTexto el contenido del primer elemento de la matriz (i = 0) sin el ahora innecesario espacio en blanco, de las siguientes fases del mismo, en las que mantenemos el procedimiento ya explicado (Else -> vTexto = vTexto & " " & mDatos(i)).

NOTAS

(1) Aunque es fácil plantearlo de este modo, en realidad se trata de una cuestión que está cargada de interrogantes, pero ahora simplemente no toca plantearlos.
(2) Supongamos que es parte de la automatización de la composición de un documento
(3) Que reconstruimos sobre la variable vTexto, por lo que inicialmente asignamos a ésta el contenido cadena vacía (vTexto = "") como forma de clarificar el funcionamiento esperado.

Textos. OOo Basic

Matrices como recurso para la entrada de datos

Con ser muy importante la mejora  que permite el [cambio de matrices por variables] para el manejo del código, no es la única ventaja que aporta, ya que esta sustitución facilita el acceso a las posibilidades que supone el uso de estructuras de iteración. En esta entrada concretaremos esa ventaja en la dase input o de solicitud de datos al usuario.



Ya hemos explicado que el uso de InputBox() es probablemente la forma más simple de resolver el problema de cómo aportar al script los datos que se necesitan para cumplir su objetivo, sea éste el que sea (1)...

vAlNom = InputBox("Nombre del alumno o alumna: ")

... pero cuando tenemos que repetir esto 20 o treinta veces esta sencillez se convierte en molesta. Aquí caben dos alternativas: crear una función o emplear un bucle.

En realidad, lo de la función no deja de ser simplificar el código, pero no evita tener que repetir la llamada a la función tantas veces como variables tengamos... o cuantos elementos tenga la matriz. Sin embargo, la segunda alternativa, el uso de un bucle, sí resuelve el tema, tanto si (además) utilizamos una función como si no. Claro que para ello es necesario utilizar matrices en lugar de variables (2).

Veamos un ejemplo:

  • Tenemos una matriz con 10 elementos -> Dim mDatos(10) As String
  • Declaramos además una variable contador -> Dim i As Integer
  • Mediante la estructura For recorremos la matriz aplicando la función InputBox() a cada elemento:
For i = 0 To UBound(mDatos())

mDatos(i) = InputBox()

Next

De este modo, y de forma sencilla, el usuario introduce los datos en cada uno de los diez elementos de la matriz sin que sea necesario repetir diez veces el mismo proceso. No obstante, tenemos un problema para el que existen dos soluciones: el usuario no sabe qué información se le solicita, así que no sabe qué introducir, ya que le falta información aclaratorio en el InputBox(), la cual sí estaba disponible en el formato anterior (InputBox("Nombre del alumno o alumna: ")).

Este problema no se resuelve en el ciclo del mismo modo que fuera de él, ya que no se solicita 10 veces (por ejemplo) el mismo dato. Debemos buscar otra solución, porque no podemos esperar que se resuelva solo recurriendo a que el usuario es conocedor del documento, ni incluso que sea la misma persona que hizo el script. 

La solución más sencilla es crear, aportar (y tener a mano en el momento adecuado) un documento informativo que asocie cada elemento de la matriz con su contenido.

Ítem 0 -> Nombre del alumno 
Ítem 2 -> Apellidos del alumno 

... En este caso sería necesaria una pequeña modificación en el InputBox() (InputBox("Ítem " & i)o similar).

La segunda opción es más costosa pero más apropiada, además de posiblemente también más funcional (3): crear una segunda matriz tipo string cuyos elementos sean sencillas cadenas de texto que informan sobre el contenido de cada elemento de la matriz de datos (4)...

Dim mConten() As String
mConten = Array("Alumno. Nombre","Alumno. Apellidos","Alumno. Fecha de nacimiento"...)

... siendo necesario modificar también el InputBox() (InputBox(mConten(i))

NOTAS

(1) Por ejemplo, la automatización de la escritura de un texto. La forma más sencilla de disponer de un dato tan simple como necesario como es el nombre del alumno es preguntarle al usuario que trabaja sobre el documento que hemos automatizado.
(2) En realidad podemos utilizar variables (y no matrices), pero necesitamos que el nombre de las matrices se asemeje al formato de los elementos de la matriz, así que no deja de ser un sucedáneo del uso de matrices.
(3) Evitamos el riesgo de que se pierda el documento que contiene el listado anterior.
(4) Aun en este caso el código es más limpio y sencillo que utilizando reiteradamente la forma primitiva de la instrucción de input

Textos. OOo Basic

Variables vs. matrices


A fuerza de necesitar conjuntos cada vez más grandes de variables en proyectos de automatización de textos, el uso alternativo de matrices ha supuesto mejoras significativas en el manejo y en la creación del script, aunque este cambio también presenta inconvenientes. Una ventaja asociada al uso de matrices es la posibilidad de trabajar con bucles.



Primero explicaré qué supone modificar la asignación de contenido a variables vs. matrices. Para ello un ejemplo sencillo es la creación de un sistema de asignación de datos mediante InputBox() (1) cuando el número de variables es ya un tanto elevado, por ejemplo cuando alcanzamos la veintena de datos que solicitamos al usuario (2).

En realidad es indiferente que esas veinte o más variables requieran todas ellas de su correspondiente interfaz, o que parte de ellas se cumplimenten "automáticamente" mediante un procedimiento de condicionalidad referenciada (3)...

If vNombreSex = "H" Then
vTratam1 = "niño"
Else 
vTratam1 = "niña"

... el hecho es que ya la simple declaración de veinte variables supone realizar un trabajo innecesario, además de complicar el manejo de este código. Por suerte tenemos una fácil solución a mano: utilizar matrices.

El resultado es un código mucho más limpio y manejable (4), aunque debemos saber que tiene un coste: perdemos en inteligibilidad del código. Usando variables, el nombre de cada una de ellas puede ser suficientemente esclarecedor respecto al dato que contiene (5), siendo ésta una recomendación que se reitera en los manuales de programación; pero cuando usamos una matriz esa posibilidad se pierde en cierta medida. 

Entiendo que en este caso puede ser muy conveniente (6) crear una documentación anexa al script que contenga una tabla en la que se indique la relación entre cada elemento de la matriz con el contenido que referencia

NOTAS

(1) Por poner un ejemplo muy socorrido y usado por su sencillez de implementación. Es dudoso que cuando el número de variables-input a manejar sea elevado, la solución interfaz sea InputBox(). En estos casos, posiblemente lo mejor y más sencillo sea trabajar con un formulario sobre una hoja de cálculo con funciones de gestor, posiblemente una solución con categoría de [docap complejo].
(2) Esa es una cantidad relativamente moderada de variables, ya que en documentos complejos (vg. dictamen de escolarización) las variables necesarias la superan con mucho. En un [ejemplo reciente], la automatización de este documento ha supuesto trabajar con un total de 63 campos/variables, y eso que sólo se trabaja con el núcleo principal del documento y no con sus dos anexos.
(3) Este es el caso de las variables que contienen datos relativos a la expresión de la concordancia gramatical de género o número. En los ejemplos de documentos con los que trabajamos, estas variables expresan normalmente la concordancia de género y se asocian al género gramatical del sujeto, muy frecuentemente a la dicotomía alumno vs. alumna.
(4) Cierto que más cercano a infinito conforme mayor sea el número de variables. LO que debemos sopesar es si interesa trabajar con una o con varias matrices, atendiendo al tipo de datos. La solución de matriz tipo variant no es ni siempre ni necesariamente la mejor opción, aunque nos permite concentrar en una única matriz los distintos tipos de datos posibles.
(5) Al dato e incluso al tipo: es posible leer Dim txtNomAl As String como variable texto (string) que contiene el nombre del alumno.
(6) También aquí, más conveniente cuando mayor sea el tamaño de la matriz. Esta tabla permitirá a otro profesional que desee trabajar con el script una mejor comprensión de su funcionamiento, evitándole una búsqueda de referencias que a veces se vuelve muy complicada.

sábado, 5 de octubre de 2024

Funciones. Matrices.

Eliminar datos repetidos

Con frecuencia trabajamos con listas o matrices que, por diversas razones, contienen datos repetidos. Estas repeticiones pueden formar parte de lo que nos interesa de los datos (su frecuencia), o ser debidas a errores en la entrada de datos de la que surgió ese listado. El tratamiento de ambas circunstancias es diferente, pero en ambos casos, en un momento determinado nos puede interesar eliminar esas repeticiones. Para ello he desarrollado esta función que, en principio, está pensada para trabajar con datos numéricos.


La función que presento aquí, ElimRep(), es una función compleja por lo que podría dividirse en partes, conformando cada una de ellas una función simple, pero como todas sus partes contribuyen al logro del objetivo, he considerado que podría ser interesante contar con una única función que realice todo el trabajo, sin que sea necesario recurrir a llamadas a terceras funciones, lo que siempre implica mayor complejidad en el desarrollo del algoritmo y, en consecuencia, mayor probabilidad de error.

El objetivo de la función queda reflejado en su nombre: eliminar los datos (aquí numéricos) que pueden estar repetidos en una lista o matriz unidimensional (matriz simple, si se prefiere). Se presupone que este procedimiento es necesario para el logro de determinado objetivo (1). El resultado esperado es una matriz simple o lista en la que cada dato sea único.

Paso a presentar el código de la función, que explico a continuación. No obstante, y aunque no suelo hacerlo, en este caso he considerado pertinente incluir un script que hace uso de la función, tanto para ejemplificar su uso (en abstracto) como para explicar los ajustes que resultaron necesarios para el desarrollo de la propia función. En resumen, lo que a continuación se muestra es un script (en rojo) y la función ElimRep(). lo que explicaré será el código de la función, pero, con fines didácticos, puedo recurrir al script para aclarar alguna cuestión. 

'Script desde el que se llama a la función ---------------------------------------

Sub DatosRepetidos

Dim mDatOr() As Integer 'Matriz original
Dim mDatTrb() As Integer 'Matriz de trabajo
Dim c As Integer

mDatOr() = Array(1,3,4,5,6,1,3,5,7,9,10,21,10,21,12,15,5,12) 'Preservamos para comparaciones

'(1,3,4,5,6,-1,-1,-1,7,9,10,21,-1,-1,12,15,-1,-1) -> Matriz resultante de la sustitución
'(1,3,4,5,6,7,9,10,21,12,15) -> Matriz resultante de la eliminación de repetidos

mDatTrb() = mDatOr() 

mDatTrb() = ElimRep (mDatTrb()) 'Llamada a la función

For c = LBound (mDatTrb()) To UBound(mDatTrb()) 'Procedimiento de visualización de datos finales
MsgBox mDatTrb(c)
Next

End Sub

'Función ----------------------------------

Function ElimRep (a())

Dim mAux() As Integer        'Variables internas de la función

Dim i As Integer, c As Integer, Dato As Integer, nR As Integer

For i = LBound (a()) To UBound(a())    ' Detección y sustitución de repetidos

Dato = a(i)

For c = i + 1 To UBound(a())
If a(c) <> -1 Then
If a(c) = Dato Then
a(c) = -1
End If
End If
Next

Next

nR = 0                                                    'Contabiliza elementos repetidos...

For i = LBound (a()) To UBound(a())
If a(i) <> -1 Then
nR = nR + 1
End If
Next

ReDim mAux(nR-1)                            '... y redimensiona la matriz auxiliar

i = 0

c = 0

For i = LBound (a()) To UBound(a())    'Asigna a la matriz aux. los elementos no repetidos
If a(i) <> -1 Then
mAux(c) = a(i)
c = c+1
End If
Next

ElimRep = mAux()                        'Retorno

End Function 

La función ElimRep () recibe como único parámetro una matriz que contiene elementos posiblemente repetidos. Como variables privadas cuenta con una matriz auxiliar o de trabajo (mAux()) y cuatro variables Integer: dos con funciones de contador (i y c), una para contener el datos de la matriz que sirve de referencia para la búsqueda (Dato) y una última para redimensionar la matriz auxiliar (nR).

Su estructura presenta cierta complejidad, ya que está formada por cuatro ciclos y cuatro condicionales. Las estructuras condicionales (If) se encuentran anidadas dentro de los bucles, estableciendo condicionalidad al funcionamiento de éstos. El resultado es tres bloques ciclo-condicional que ejecutan funciones específicas.

El primer bloque es el de mayor complejidad estructural y funcional. Consta de dos ciclos anidados y dos condicionales dentro del ciclo interno y anidados a su vez. La función de este bloque es recorrer la matriz-parámetro ítem a ítem, identificando los elementos repetidos y sustituyéndolos por un dato pensado para no generar confusiones con los originales (-1), a fin de facilitar el posterior traslado condicionado a la matriz auxiliar.

El segundo ciclo consta de un ciclo que contiene un condicional y su función es recorrer la matriz-parámetro y contabilizar el número de elementos sobre la variable nR, en función de un segundo condicional que excluye los valores repetidos (identificados por su contenido: -1. De este modo obtenemos el dato que nos servirá para redimensionar la matriz auxiliar sin necesidad de tener que aplicar posteriormente y de forma reiterada, las instrucciones ReDim Preserve (2).

Finalmente, el tercer grupo, al igual que el anterior, consta de un ciclo que anida un condicional. Su función es trasladar a la matriz auxiliar (mAux()), previamente redimensionada según el valor de nR (ReDim mAux(nR-1)) (3), el contenido de la matriz-parámetro que no contiene como dato el establecido como sustituto de los valores repetidos, esto es, -1.

Finaliza la matriz con el Return ElimRep = mAux() que al ser devuelto al script sobre la matriz secundaria (de trabajo), sustituye completamente a ésta, siendo que originalmente quedó conformada como copia de la matriz principal (mDatTrb() = mDatOr()). Este modo de proceder obedece al objetivo didáctico de este ejemplo  (4).

El bloque crítico es, como dije, el primero de los tres, que posiblemente requiera una explicación más detallada de su funcionamiento.

  • El primer ciclo, o ciclo principal, posicionado en el exterior de la estructura, sobre el contador i, recorre las posiciones de la matriz se trabajo y asigna a Dato el contenido de cada una de ellas (Dato = a(i)). 
  • Gracias al segundo ciclo, anidado en el primero y que se rige por el contador c, realizamos un segundo recorrido sobre las posiciones de la matriz. Dentro de este bucle actúan las dos condiciones anidadas, que son las responsables de el que proceso se desarrolle correctamente.
  •  La primera condición nos asegura que no actuamos sobre las posiciones con el valor sustitutivo (-1) (If a(c) <> -1 Then)
  • Y la segunda que se ejecuta la sustitución (a(c) = -1) cuando se da la condición correcta (If a(c) = Dato Then)
El resultado que se obtiene al finalizar este proceso es que matriz-parámetro mantiene los elementos  no repetidos, siendo sustituidos paulatinamente los que se repiten por el valor -1. Sobre el conocimiento de este hecho actúan los dos bloques que siguen, según las funciones respectivas, que describí antes.

NOTAS

(1) No interesa aquí cual pueda ser éste, pero evidentemente tiene que ajustarse a la naturaleza de los datos y del procedimiento que se quiere desarrollar. El ejemplo más simple es eliminar errores, pero no es el único.
(2) Lo que sería la alternativa lógica, pero que no genera demasiada confianza por la posibilidad de que provoque la pérdida de datos previos.
(3) Según lo dicho antes. A nR se le aplica el consabido factor de corrección (nR-1), en función de índice inicial de las matrices en OOo Basic.
(4) En una formulación real no sería precisa esta matriz de trabajo, pero aquí nos sirve para poder monitorizar el proceso y comparar los resultados obtenidos en las diferentes fases del mismo con lo esperado según el modelo de datos implementados en la matriz principal. Este es también el objetivo de los comentarios que contienen los datos de la matriz tras la sustitución de los elementos repetidos y tras su eliminación.

viernes, 4 de octubre de 2024

Funciones. Matrices

Borrar posiciones de una matriz.

En [esta entrada] hablamos del intercambio de datos entre matrices desiguales (de tamaño diferente) y mostré en ella una función (mejor dicho, dos funciones) que permitían reducir el tamaño de una matriz atendiendo a un criterio dado, concretamente a la ausencia de contenido ("") en determinadas posiciones. Quedaba pendiente la explicación de ambas, así que empecemos por la primera (1).



La formulación original de la función es la siguiente (2):

Function aRemoveEmpty ( ByRef a() )

   Dim n As Long, u As Long, l As Long

   u = UBound(a)
   l = LBound(a)

   For n = u To l Step -1
      If Trim(a(n))= "" Then a = aDel(a, n) 'Llama a la función aDel() si se da la condición ""
   Next

   aRemoveEmpty = a

End Function

Lo primero que observamos es que en la declaración del parámetro de la función empleamos la expresión ByRef para especificar el modo en que se trabaja con la función. Sobre esta cuestión ver aclaración [en  esta entrada] (3). Mediante el parámetro a() desde la función accedemos  a la matriz que vamos a pasar desde el script (en este caso, [como sabemos], la matriz mRecurso() en mRecurso()=  aRemoveEmpty (mRecurso())).

En segundo lugar se han declarado tres variables (Dim n As Long, u As Long, l As Long), todas ellas de tipo Long, aunque si las tratamos como Integer podría ser suficiente (4), dos de ellas (l y u, respectivamente) para asignar el valor posición inicial (LBound()) y final (UBound()) de la matriz-parámetro (como veremos a continuación) y la tercera (n) como contador del bucle que vamos a crear.

Salvo esta tercera (el contador), las otras dos podrían ser obviadas si usamos las referencias directas a ambas posiciones en el bucle. Veremos la alternativa más abajo. No obstante, de este modo se simplifica la formulación de los límites del bucle, quedando éste simplificado y más claramente formulado.

Tras a asignación de contenido a las variables l y u, se formula un bucle que tiene dos particularidades, derivando la segunda de la primera:
  • Que se organiza en sentido fin-inicio (en lugar del orden normal), esto es desde UBound(a()) hasta LBound(a()) (5)
  • Y, en consecuencia, el recorrido se hace también en orden inverso mediante Step -1)
Esto supone que el bucle (For n = u To l Step -1) recorre la matriz-parámetro desde su posición final (u - > u = UBound(a)) hasta su posición inicial (l ->  l = LBound(a)) pero en orden inverso, esto es, n->...->2-> 1Ambas especificaciones son necesarias para el correcto funcionamiento de la función. La segunda por derivar necesariamente de la primera (6) y la primera (el orden de las variables) por ser necesaria para la coherencia de la función y de la función a la que ésta llama (aDel()) (7).

Lo que sí podríamos hacer para "simplificar" el script es prescindir de las variables u y l y utilizar directamente sus referencias de valor, esto es, sustituir For n = u To l Step -1 por For n = UBound(a) To LBound(a) Step -1, con lo que no necesitaríamos más que la variable contador (n en este caso) (8)

El bucle contiene un condicional If que resulta clave para la lógica de la función: sólo si de da la condición x se llama a la función aDel(), lo que equivale a decir que si se da esa condición, y sólo si de da, se elimina la posición y con ella el elemento (dato) que la ocupa. Este x es, en nuestro caso que el dato sea "" (o sea, que la posición de la matriz no contenga ningún dato.

 If Trim(a(n))= "" Then a = aDel(a, n) (9)

Llama la atención que la estructura condicional carezca de la instrucción que la cierra (End If), así como que la consecuencia del cumplimiento de la condición se escriba a continuación de Then. La sintaxis estándar del condicional sería la siguiente:

If Trim(a(n))= "" Then 

    a = aDel(a, n)

End If


NOTAS

(1) Reitero la página de procedencia, [wiki.open-office.es], donde puedes encontrar, además de éstas, otras funciones para trabajar con matrices... y en el sitio muchas más cosas. Excelente contribución.
(2) Al ser una función que llama a otra función, son necesarias ambas para el correcto funcionamiento del script, así que no es posible emplearla de forma aislada. Digo esto para evitar "sorpresas desagradables.
(3) Como ya sabemos, es posible omitir el identificador del modo de pase de datos a la función, ya que en OOo Basic ByRef es el modo por defecto. En consecuencia, la declaración de la función podría ser Function aRemoveEmpty (a()) sin que se altere su funcionamiento. No obstante, me parece pertinente mantener la formulación original, ya que ayuda a comprender mejor el funcionamiento de aRemoveEmpty().
(4) Se usa Long para evitar errores en caso de que la matriz-argumento fuera muy grande, que no suele ser el caso, de ahí que sea perfectamente funcional declarar esas variables como Integer.
(5) El uso de variables en lugar de la formulación directa puede despistarnos a este respecto.
(6) Su ausencia, que implica el uso de su contrario (Step +1) no hace fracasar a la función, pero ésta no obtiene el resultado esperado.
(7) Que trataremos en otra entrada.
(8) Que por tradición se suele identificar como i o como conta. Yo prefiero i. En ese caso la línea de declaración de variables de la función quedaría simplificada a Dim i As Integer
(9) La función Trim() sirve para eliminar los posibles espacios vacíos que pudiera tener el dato que ocupa la posición n de la matriz a() (Trim(a(n)))

Funciones. Matrices.

Frecuencias

Una vez que ya sabemos [buscar elementos] en una matriz, estamos en disposición de desarrollar funciones para determinar el número de veces que se repite ese elemento o dato (su frecuencia) y la posición o posiciones que ocupa.


La función que nos permiten calcular la frecuencia de un dato en una matriz es, en realidad, una derivación de la función que nos permite comprobar su presencia en ella (bPos()), lo que no deja de ser la forma más simple de frecuencia (f=0 vs. f=1). Por ello nos basaremos en esa función para desarrollar la actual. Más compleja es la función que nos devuelva las posiciones que ese dato ocupa en la matriz, pero ya tendremos tiempo de hablar de ella.

Empecemos por la función que nos permite conocer el número de veces que un dato se presenta en una matriz que como ya dije y podemos comprobar, es similar (incluso más sencilla) a bPos(). 

Function fDato (ByRef a(), sDt As String) As Integer

Dim c As Integer, f As Integer

f = 0

For c = 0 To UBound(a())
If a(c) = sDt Then
f = f + 1
End If
Next

fDato = f

End Function

Vemos que recibe dos parámetros, la matriz de referencia (a()) y el dato a buscar (sDt), y devuelve un valor numérico. Además cuenta con dos variables privadas, un contador para recorrer la matriz-parámetro (c) y otro para acumular la repetición de la presencia del dato en ella (f).

El proceso que desarrolla es muy simple:
  • Recorre la matriz-parámetro hasta su último elemento mediante un bucle (For c = 0 To UBound(a()))
  • Y, mediante un condicional, busca las coincidencias que se encuentran en el contenido de la matriz valorando su igualdad con el dato de referencia (sDt) pasado como argumento (If a(c) = sDt Then).
Dado que hemos asignado el valor inicial f = 0 al contador de la frecuencia, si el dato buscado no se encuentra en la matriz, la función devolverá 0, que en este caso es un dato de frecuencia, no un índice de posición; pero si el dato sí se encuentra en la matriz se produce el incremento de f mediante la expresión subordinada (f = f + 1) al condicional. 

Este proceso se repite hasta finalizar el recorrido por la matriz-parámetro, finalizando la función con el Return correspondiente (fDato = f).

Para muchos análisis, esta función puede ser suficiente, pero podemos necesitar conocer el índice o posición que ocupa cada una de las repeticiones del dato dentro de la matriz. Para ello deberemos desarrollar una segunda función ( pDatos())

Function pDatos ( ByRef a(), sDt As String)

Dim mF () As Integer
Dim c As Integer, f As Integer, nV As Integer

f = fDato (a(), sDt)

If f >= 1 Then 
ReDim mF(f-1)
nV = 0
For c = 0 To UBound(a())
If a(c) = sDt Then
mF(nV) = c
nV = nV + 1
End If
Next
Else
ReDim mF(0)
mF(0) = -1
End If

pDatos = mF()


End Function

Esta segunda función recurre a la primera (fDato()) para establecer la dimensión de su matriz privada (auxiliar), por lo que en realidad es una función complementaria y, si se quiere, derivada de aquella.

Recibe los mismos parámetro que la primera (y de hecho se los pasa a su vez como parámetros a ésta) y devuelve una matriz con datos numéricos que representan las posiciones en que se sitúa el dato de referencia (sDt) que, junto con la matriz-base (a()) también recibe del script principal.

Como privadas precisa una matriz auxiliar (mF ()) y tres variables (Dim c As Integer, f As Integer, nV As Integer). La matriz auxiliar es el contenido de su Return y las variables son un contador para el bucle (c), una variable receptora del Return de la función fDato() (f) y una última variable (nV) como índice para recorrer su función auxiliar. Veremos su lógica en el análisis de la función, a continuación.

Lo primero que hacemos es solicitar la acción de la función fDato () cuyo Return asignamos a la variable f (f = fDato (a(), sDt)), el cual usamos para redimensionar su matriz auxiliar (ReDim mF(f-1)) (1).

Sobre esta base desarrollamos el proceso central de la función, que se estructura de la siguiente manera:

Mediante un condicional se controla el efecto de la presencia/ausencia del dato en la matriz-parámetro. En caso de que este dato esté presente en dicha matriz (If f >= 1 Then), se desarrolla el proceso que se explica a continuación. Pero en caso de que no lo esté se dimensiona la matriz auxiliar y se asigna como contenido y único el valor -1 (Else -> ReDim mF(0) -> mF(0) = -1) (2

Dentro de la primera parte del condicional anterior, mediante un bucle se recorre la matriz-parámetro (For c = 0 To UBound(a())) haciendo uso del contador c. Este bucle contiene a su vez un segundo condicional que analiza la presencia del dato-parámetro en la matriz-parámetro (If a(c) = sDt Then).

Cuando su resultado es True (3) se asigna a la posición nV de la matriz auxiliar el valor c en ese momento del ciclo (mF(nV) = c), generándose a continuación el incremento +1 de ese contador secundario nV (nV = nV + 1). Esto permite recorre la matriz secundaria, asignando a sus posiciones, y como datos, los valores posicionales del dato buscado en la matriz-parámetro (4).

Finaliza la función con su Return (pDatos = mF()), lo que implica que el script principal cuenta con información sobre las posiciones que el dato solicitado ocupa dentro de la matriz principal. En caso de que no exista este dato se devuelve una

NOTAS

(1) Obsérvese que la dimensión de esta matriz (mF()) viene determinada por el número de veces que la matriz-parámetro (a()) contiene el dato (resultado de la función fDato ()) menos 1 ((f-1)). Esto es debido, de nuevo, a que el primer valor de posición de una matriz es 0, por lo que el valor f implicaría sobredimensionar la matriz auxiliar, con el consiguiente fallo de funcionamiento de la misma.
(2) De este modo, el script principal recibe como Return una matriz de un único elemento que contiene un valor específico (-1) y puede procesar el resultado obtenido sin confusión en cuanto a su significado. Evitamos de paso que se produzca un error en el funcionamiento de la función.
(3) Esto es: cuando efectivamente en la posición c de la matriz-parámetro (a(c)) se encuentra el dato buscado(a (a(c) = sDt)
(4) La variable-contador secundario nV ha sido igualada a 0 con anterioridad al bucle (nV = 0) a fin de que el inicio del recorrido de las posiciones de la matriz auxiliar (mF(nV)) comience en esta posición con toda seguridad.