miércoles, 2 de octubre de 2024

Funciones. Matrices.

Añadir un elemento al final de la matriz

Bien para aumentar un elemento, bien para completar el contenido de una matriz declarada inicialmente como matriz vacía, es necesario disponer de una función que nos permita añadir elementos a la matriz. OOo Basic no dispone de una función integrada que realice esta tarea, pero podemos crearla.


La comunidad [Open Office] ha publicado una función con este objetivo: aumentar el contenido de una matriz, añadiendo un elemento al final de la misma (1). Te la muestro y explico a continuación:

Function aAdd(ByRef a(), ByVal xValor)

   Dim u As Integer
   u = UBound(a()) + 1
   ReDim Preserve a(u)
   a(u) = xValor
   aAdd = a ()

End Function

Aparentemente su funcionamiento es muy sencillo: aAdd() recibe dos parámetros obligatorios: el primero la matriz original (ByRef a()) y el segundo (ByVal xValor) el dato a añadir. Además cuenta con una variable privada (u) cuyo cometido es facilitar el cálculo de la nueva dimensión de la matriz, la cual se calcula haciendo uso de la función UBound() sobre la matriz-parámetro, valor al que se añade una unidad (u = UBound(a()) + 1).

Posteriormente se recalcula el tamaño de la matriz-parámetro mediante instrucciones (ReDim Preserve a(u)), haciendo uso del valor de u. Precisamente sobre el índice u se ejecuta la asignación del nuevo dato o contenido a la matriz redimensionada (a(u) = xValor) recurriendo, ahora, al segundo parámetro de la función, la cual finaliza con el return correspondiente (aAdd = a ())

En principio, la lógica de esta función es clara y correcta, pero, por desgracia el comportamiento de las instrucciones ReDim Preserve no es el esperado, produciéndose el vaciado de contenido de la matriz (2): el resultado es que, al aplicar la función obtenemos una matriz redimensionada, pero vacía.

Para superar esta muy seria limitación, he desarrollado una función alternativa (aFin()) que tiene el mismo objetivo, pero que elude el uso de la instrucción Preserve y modifica la lógica de la función "original". Si sintaxis es la siguiente:

Function aFin (ByRef mP(), ByVal fVal)

Dim mS () As String
Dim i As Integer
Dim u As Integer

u = uBound(mP()) + 1
ReDim mS(u)

For i = LBound(mP()) To UBound(mP())
mS(i) = mP(i)
Next

mS(u) = fVal

aFin = mS()

End Function 

En cuanto a los parámetros, aFin() no se diferencia de aAdd(), como se puede ver (3), pero sí en lo relativo a las variables privadas: en aFin() se declaran una función auxiliar (mS()) y dos variables Integer, una (u) con idéntica función (y denominación) que en aAdd(), y otra (i) con función de contador para la implementación de un bucle For.

La primera acción que se desarrolla es el mismo proceso que en aAdd(), obtener sobre u la dimensión de la matriz, en base al valor que devuelve UBound() de la matriz-parámetro (u = uBound(mP()) + 1), como valor para redimensionar una matriz; pero con dos modificaciones de interés:
  • La que se redimensiona es la matriz auxiliar (mS(u)), la matriz-parámetro, por lo que se evita que ésta pierda su contenido.
  • Y sólo se usa la instrucción ReDim, ya que estamos hablando de una matriz vacía de contenido (ReDim mS(u))
La segunda fase consiste en pasar los valores de la matriz-parámetro (que ésta conserva) a la matriz auxiliar (mS(i) = mP(i)) , para lo que se recurre a un ciclo determinado por los valores LBound() y UBound() de la matriz parámetro (For i = LBound(mP()) To UBound(mP())) (4). Esta fase se complementa con la asignación del valor del segundo parámetro como contenido añadido a la matriz auxiliar (mS(u) = fVal).

Finalmente el return se realiza sobre esa matriz (aFin = mS())

Ahora te toca a ti idear un script que te permita comprobar el funcionamiento efectivo de ambas funciones, en las que, si lo deseas, puedes introducir modificaciones que las hagan más eficientes.

NOTAS

(1) Entre otras cosas, esto corrobora que efectivamente OOo Basic no dispone de una función Built-In que cumpla este objetivo. Siendo algo tan básico y necesario, no deja de ser ésta una carencia significativa.
(2) Esta "inconsistencia" ha sido detectada por diferentes autores, incluyendo a los mismos [creadores de esta función]. Por mi parte he comprobado que en ocasiones funcionan correctamente (redimensionan y mantienen el contenido original. [Ver aquí]), pero no en otras, como es el caso de la función aAdd(). Lo que no he encontrado en la literatura es una explicación del motivo, tan sólo la constatación un tanto sorprendida del fallo en el funcionamiento, que siempre es el no mantenimiento del contenido previo, lo que implica que la que no funciona como debiera es la instrucción Preserve, ya que ReDim sí lo hace según lo esperado.
(3) Sí modifico su denominación (Function aFin (ByRef mP(), ByVal fVal)), pero esto es secundario.
(4) Inicialmente intenté pasar los valores directamente (mS() = mP()), pero mS() perdía la dimensión dada mediante ReDim, por lo que no resultó viable. Al usar un bucle ajustado a la dimensión de mP(), el paso de datos se resuelve correctamente.