martes, 1 de octubre de 2024

OOo Basic. Matrices.

Tamaño de una matriz

Lo normal es declarar una matriz incluyendo su dimensión, pero no siempre es ni conveniente ni necesario. En OOo Basic contamos con una instrucción que nos facilita modificar el tamaño de una matriz en función de nuestras necesidades, lo que da mayor flexibilidad a nuestros script.


Estamos hablando de la ReDim, que complementa y modifica lo establecido con la instrucción Dim, que empleamos en la declaración tanto de las variables como de las matrices.

Mientras que con Dim declaramos la matriz y establecemos el tipo de elementos que contiene y, si lo deseamos, cuantos elementos contiene, con ReDim (redimensionar) modificamos (o establecemos, según el caso) esa cuantificación.

La instrucción ReDim hace referencia a la matriz objeto de modificación y establece en su paréntesis el valor numérico (Integer, por lo general) que indica el valor de posición de los elementos de la matriz y, en consecuencia, redimensiona la propia matriz. Esto resulta útil cuando no dimensionamos (o dimensionamos como mMatriz ()) la matriz al declararla, como cuando necesitamos modificar la dimensión inicial dada, tanto para disminuirla como para aumentarla.

Veamos algunos ejemplos para entender mejor el uso de la instrucción ReDim

Supongamos que creamos un scritp en el que declaramos una matriz, pero no establecemos una dimensión inicial (Dim mMatriz1() As String). Por ejemplo:

Sub ModificaMatrizA

Dim mMatriz1() As String
Dim i As Integer

mMatriz1(0) = "Pelo"

For i = 0 To 5
mMatriz1(i) = CStr(i)
Next

End Sub

En este caso,  tanto la instrucción (mMatriz1(0) = 1) como la instrucción inserta en el bucle (mMatriz1(i) = i) nos darán error (El índice está fuera del intervalo establecido).

Para evitar este error deberemos redimensionar previamente la matriz, lo cual podemos hacer directamente y de forma estática...

ReDim mMatriz1(0)
mMatriz1(0) = "Pelo"

... o de forma dinámica, recurriendo a la acción del bucle (1).

For i = 0 To 5
ReDim mMatriz1(i)
mMatriz1(i) = CStr(i)
Next

La segunda situación en la que nos es útil la instrucción ReDim es cuando necesitamos modificar el tamaño inicialmente dado a una matriz. En este caso podemos ampliar el tamaño inicial o reducirlo. La primera opción implica aumentar el contenido de la matriz por resultar insuficiente el inicialmente pensado y la segunda es útil cuando necesitamos sólo una parte (tomada desde el inicio) del contenido de esa matriz.

Estas explicaciones son un tanto simples y a la vez abstractas, y sólo concreciones prácticas pueden dar a ambas funcionalidades reales, pero aquí me debo limitar a este tipo de planteamiento tanto por el objetivo de la entrada como por no hacerla más extensa de lo necesario.

Supongamos que hemos creado una matriz (o un conjunto de ellas), inicialmente dimensionadas a 5 elementos (por ejemplo Dim mMatrizA(4) As String) y que el desarrollo del algoritmo (2) conlleva la necesidad de ampliar ese tamaño hasta los 10 elementos. En este caso es suficiente con aplicar a la matriz la instrucción ReDim para configurarla adecuadamente (ReDim Preserve mMatrizA(9)).

El problema es que, al hacerlo nos encontramos con la desagradable sorpresa de que la matriz está vacía. Para evitar que esto suceda es necesario añadir la instrucción Preserve (ReDim Preserve mMatrizA(9)): ahora se mantienen los elementos iniciales y se añaden cinco posiciones más (desde mMatrizA(5) hasta mMatrizA(9)) en las que podremos incluir nuevos contenidos. A continuación te dejo un script que lo ejemplifica (3):

Sub ModificaMatrizB

Dim mMatrizA(4) As String
Dim i As Integer

mMatrizA(0) = "Pedro"
mMatrizA(1) = "Luis"
mMatrizA(2) = "Carlos"
mMatrizA(3) = "Ramón"
mMatrizA(4) = "Jaime"

For i = 0 To UBound(mMatrizA())
MsgBox i & " " & mMatrizA(i)
Next

ReDim Preserve mMatrizA(9)

For i = 0 To UBound(mMatrizA())
MsgBox i & " " & mMatrizA(i)
Next

End Sub

Algo similar ocurre en caso de reducir el tamaño original: lo esperado es que simplemente se reduzca el tamaño de la matriz y que se mantengan los elementos no eliminados, pero de nuevo ReDim vacía de contenido nuestra matriz y, de nuevo, necesitamos usar también la instrucción Preserve para que esto no ocurra (4).

Sub ModificaMatrizC

Dim mMatrizA(4) As String

Dim i As Integer

mMatrizA(0) = "Pedro"

mMatrizA(1) = "Luis"

mMatrizA(2) = "Carlos"

mMatrizA(3) = "Ramón"

mMatrizA(4) = "Jaime"

For i = 0 To UBound(mMatrizA())

MsgBox i & " " & mMatrizA(i)

Next

ReDim Preserve mMatrizA(2)

For i = 0 To UBound(mMatrizA())

MsgBox i & " " & mMatrizA(i)

Next

End Sub

El "descubrimiento" del efecto no esperado de la instrucción ReDim nos obliga a replantear el código del primer script (Sub ModificaMatrizA), ya  que si añadimos un buble final que recorra el contenido de la matriz, podremos observar que está vacía, a excepción del último dato, que se conserva porque no se ve afectado por la instrucción ReDim, ya que es previa a que pasemos el dato.

También en este script necesitamos incluir la instrucción Preserve para que no se pierdan los datos (ReDim mMatriz1(i)  -> ReDim Preserve mMatriz1(i)), así que te sugiero reescribir este script para que su funcionamiento se ajuste a lo esperado. También estaría bien que incluyeras ese bucle final que te permita comprobar el funcionamiento sin y con Preserve.

NOTAS


(1)  En este caso, que resulta muy interesante, en cada recorrido del ciclo redimensionamos la matriz, de modo que vamos incrementando su tamaño conforme vamos recorriendo el ciclo. Esto evita que se produzca el error que se producía antes. Este procedimiento, además de un vicio oculto que veremos al final de la entrada, tiene como limitación que debemos saber de antemano cual es el límite de recorrido del ciclo (For i = 0 To 5), lo que condiciona el tamaño que tendrá la matriz. Si deseamos que sea éste el que condicione el ciclo deberemos redimensionar la matriz antes de iniciar el bucle (con lo que estamos en el caso anterior, aquel que denominamos "forma estática"). También sería posible crear un procedimiento totalmente dinámico, pero necesitaríamos recurrir a un anidamiento de estructuras, algo que sobrepasa los objetivos de aprendizaje propios de esta entrada.
(2) Este planteamiento no es precisamente ni correcto ni lógico, pero nos sirve a fines didácticos.
(3) El primer bucle te muestra el contenido inicial de la matriz (5 elementos) y el segundo el resultado de incrementar el tamaño de la matriz (10 elementos, los cinco primeros y cinco más vacíos). Puedes comprobar qué sucede si eliminas la instrucción Preserve (ReDim Preserve mMatrizA(9)) o si modificar el tamaño deseado o el valor inicial del contador del bucle.
(4) Mediante ReDim Preserve mMatrizA(2) hemos eliminado de la matriz original los elementos mMatrizA(3) y mMatrizA(4), pero conservamos los tres primeros. Si no usamos Preserve, la matriz queda redimensionada a tres elementos, pero no conserva los datos de esos tres elementos. Puedes comprobarlo eliminando esa instrucción (ReDim mMatrizA(2)). Observa ahora el output del segundo ciclo. También te puede interesar comparar el funcionamiento de este segundo ciclo en este script y en el anterior, con y sin la instrucción Preserve.