Dynamic array/es: what changed
Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.
Title: Dynamic array/es
| Old | New | Line | |
|---|---|---|---|
| 1 | + | Una **matriz dinámica** es una [[Array|matriz]] cuyas dimensiones no son conocidas hasta que se usan, es decir no se saben en el momento de [[Compile time|compilar]] el programa. EL tipo matriz dinámica no es el único tipo que puede proveer matrices de longitud variable, pero hasta hoy es el único que admite el compilador [[FPC]]. | |
| 2 | + | ||
| 3 | + | ## Utilización | |
| 4 | + | ||
| 5 | + | ### Concepto | |
| 6 | + | ||
| 7 | + | La definción de la matriz dinámica únicamene reserva el espacio en memoria para un [[Pointer|puntero]]. | |
| 8 | + | ||
| 9 | + | Durante la [[runtime|ejecución]] diversas rutinas asegurarán un uso adecuado pero, y lo que es más importante, la sintaxis de acceso a los elementos de una matriz colocando índices entre corchetes es compatible con el [[Compiler|compilador]], implementado como desreferenciación automática del puntero. | |
| 10 | + | ||
| 11 | + | Los índices de las matrices dinámicas son siempre números enteros no negativos que comienzan en cero para el primer elemento. No es posible utilizar un tipo enumerado o cualquier otro tipo ordinal como índice. El primer elemento siempre es de índice `0` ; esto no se puede cambiar. | |
| 12 | + | ||
| 13 | + | ### Definición | |
| 14 | + | ||
| 15 | + | Una matriz dinámica de una dimensión se define así: | |
| 16 | + | ||
| 17 | + | ``` pascal | |
| 18 | + | array of <type>; | |
| 19 | + | ``` | |
| 20 | + | ||
| 21 | + | No se especifican el tamaño de dimensiones, esta es la diferencia con la definición de una matriz corriente. | |
| 22 | + | ||
| 23 | + | Para definir una matriz multidimensional, se especifica una matriz como el tipo base, creando así una "matriz de matrices": | |
| 24 | + | ||
| 25 | + | ``` pascal | |
| 26 | + | array of array of <type>; | |
| 27 | + | ``` | |
| 28 | + | ||
| 29 | + | ### Dimensiones | |
| 30 | + | ||
| 31 | + | El [[Procedure|procedimiento]] del compilador[`setLength`](https://www.freepascal.org/docs-html/rtl/system/setlength.html) se usa para cambiar las dimensiones de una matriz dinámica, siempre que haya suficiente memoria. | |
| 32 | + | ||
| 33 | + | ``` pascal | |
| 34 | + | program DimesionarDemo(input, output, stdErr); | |
| 35 | + | var | |
| 36 | + | cedazo: array of longWord; | |
| 37 | + | begin | |
| 38 | + | setLength(cedazo, 1337); | |
| 39 | + | end. | |
| 40 | + | ``` | |
| 41 | + | ||
| 42 | + | El procedimiento reserva memoria para 1337 elementos del tipo especificado y para los datos necesarios para manejarlos. Los elementos preexistentes de la matriz se conservan, y los nuevos elementos se inicializan con el valor `predeterminado` (`default`) del tipo de los elementos de la matriz | |
| 43 | + | ||
| 44 | + | Es importante remarcar que, dado que todos los elementos se copian con `setLength`, puede ser extremadamente ineficiente con matrices grandes o dentro de bucles internos. | |
| 45 | + | ||
| 46 | + | A las matrices multidimensionales también se les asignan sus dimensiones, y pueden ser redimensionadas, con `setLength`, especificando las dimensiones necesarias de esta forma: | |
| 47 | + | ||
| 48 | + | ``` pascal | |
| 49 | + | program multidimensionalSetLengthDemo(input, output, stdErr); | |
| 50 | + | var | |
| 51 | + | ejemplos: array of array of smallInt; | |
| 52 | + | begin | |
| 53 | + | setLength(ejemplos, 12, 64); | |
| 54 | + | end. | |
| 55 | + | ``` | |
| 56 | + | ||
| 57 | + | En este ejemplo, los indices válidos para la matriz `ejemplos` serán, para la primera dimensión el rango `0..11`, y para la segunda dimensión eñ rango `0..63`. | |
| 58 | + | ||
| 59 | + | Un hecho bastante útil es que, debido a que las matrices dinámicas multidimensionales son siempre “matrices de matrices”, la limitación de que todas las dimensiones deben ser del mismo tamaño no se aplica a ellas. Las diferentes dimensiones se implementan como matrices y cada una puede tener su propio tamaño. | |
| 60 | + | ||
| 61 | + | Por ejemplo: | |
| 62 | + | ||
| 63 | + | ``` pascal | |
| 64 | + | program PotenciaBinomica(input, output, stdErr); | |
| 65 | + | var | |
| 66 | + | TrianguloDePascal: array of array of longWord; | |
| 67 | + | exponente: longInt; | |
| 68 | + | factor: longInt; | |
| 69 | + | begin | |
| 70 | + | setLength(TrianguloDePascal, 20); | |
| 71 | + | ||
| 72 | + | setLength(TrianguloDePascal[0], 1); | |
| 73 | + | TrianguloDePascal[0][0] := 1; | |
| 74 | + | ||
| 75 | + | setLength(TrianguloDePascal[1], 2); | |
| 76 | + | TrianguloDePascal[1][0] := 1; | |
| 77 | + | TrianguloDePascal[1][1] := 1; | |
| 78 | + | ||
| 79 | + | // Contruimos los valores por simple adición | |
| 80 | + | for exponent := 2 to high(TrianguloDePascal) do | |
| 81 | + | begin | |
| 82 | + | setLength(TrianguloDePascal[exponente], exponent + 1); | |
| 83 | + | TrianguloDePascal[exponente][0] := 1; | |
| 84 | + | TrianguloDePascal[exponente][exponente] := 1; | |
| 85 | + | for factor := 1 to exponente - 1 do | |
| 86 | + | begin | |
| 87 | + | TrianguloDePascal[exponente][factor] := | |
| 88 | + | TrianguloDePascal[exponente - 1][factor - 1] + | |
| 89 | + | TrianguloDePascal[exponente - 1][factor]; | |
| 90 | + | end; | |
| 91 | + | end; | |
| 92 | + | ||
| 93 | + | // ... | |
| 94 | + | ``` | |
| 95 | + | ||
| 96 | + | ### initializing | |
| 97 | + | ||
| 98 | + | [[FPC New Features 3.0.0|Desde FPC 3.0.0]] los tipos de matrices dinámicas que no son anónimos son dotadas automáticamente de un "constructor", como es habitual en la programación orientada a objetos. Esto permite una llamada implícita a `setLength` con una serie de asignaciones en una única sentencia: | |
| 99 | + | ||
| 100 | + | ``` pascal | |
| 101 | + | program CrearMatrizDinamicaDemo(input, output, stdErr); | |
| 102 | + | type | |
| 103 | + | verdades = array of boolean; | |
| 104 | + | var | |
| 105 | + | l: verdades; | |
| 106 | + | begin | |
| 107 | + | l := verdades.create(false, true, true, false, true, false, false); | |
| 108 | + | end. | |
| 109 | + | ``` | |
| 110 | + | ||
| 111 | + | Esto es aplicable a las matrices de matrices, obviamente: | |
| 112 | + | ||
| 113 | + | ``` pascal | |
| 114 | + | program CrearMatricesDinamicasAnidadasDemo(input, output, stdErr); | |
| 115 | + | type | |
| 116 | + | verdades = array of boolean; | |
| 117 | + | patron = array of verdades; | |
| 118 | + | var | |
| 119 | + | p: patron; | |
| 120 | + | begin | |
| 121 | + | p := patron.create( | |
| 122 | + | verdades.create(false, false), | |
| 123 | + | verdades.create(true, false), | |
| 124 | + | verdades.create(true, false, false), | |
| 125 | + | verdades.create(true, true, false) | |
| 126 | + | ); | |
| 127 | + | end. | |
| 128 | + | ``` | |
| 129 | + | ||
| 130 | + | ### Manejando | |
| 131 | + | ||
| 132 | + | Debemos tener en cuenta que las matrices dinámicas son punteros. La asignación de variables de matriz dinámica entre sí no copia ninguna carga útil, los datos en sí, si no solo la dirección de la matriz. Esto difiere del comportamiento de las matrices estáticas. | |
| 133 | + | ||
| 134 | + | Si queremos duplicar los datos, usaremos [`system.copy`](https://www.freepascal.org/docs-html/rtl/system/copy.html). | |
| 135 | + | ||
| 136 | + | ``` pascal | |
| 137 | + | program CopiaMatricesDinamicas(input, output, stdErr); | |
| 138 | + | ||
| 139 | + | var | |
| 140 | + | algo, tasca: array of char; | |
| 141 | + | ||
| 142 | + | procedure ImprimeMatrices; | |
| 143 | + | begin | |
| 144 | + | writeLn('algo[0] = ', algo[0], '; tasca[0] = ', tasca[0]); | |
| 145 | + | end; | |
| 146 | + | ||
| 147 | + | begin | |
| 148 | + | setLength(algo, 1); | |
| 149 | + | algo[0] := 'X'; | |
| 150 | + | // copia la referencia | |
| 151 | + | tasca := algo; | |
| 152 | + | write(' valores iniciales: '); | |
| 153 | + | ImprimeMatrices; | |
| 154 | + | ||
| 155 | + | // cambiar datos con segunda referencia | |
| 156 | + | tasca[0] := 'O'; | |
| 157 | + | write('cambio con 2ª ref: '); | |
| 158 | + | ImprimeMatrices; | |
| 159 | + | ||
| 160 | + | // copiar contenido | |
| 161 | + | tasca := copy(algo, 0, length(algo)); | |
| 162 | + | tasca[0] := 'X'; | |
| 163 | + | write(' copiado y cambiado: '); | |
| 164 | + | ImprimeMatrices; | |
| 165 | + | end. | |
| 166 | + | ``` | |
| 167 | + | ||
| 168 | + | Únicamente usando `copy` ambas matrices se pueden modificar de forma independiente. | |
| 169 | + | ||
| 170 | + | Como se indicó anteriormente, `setLength` *copia* datos. Este procedimiento se puede usar para hacer que los datos sean únicos, a pesar de que solo se han copiado las referencias, los punteros (así que solo las direcciones) (`tasca := algo`). La línea resaltada en el ejemplo anterior podría reemplazarse por `setLength(tasca, length(tasca))`, aunque la función `copy` es preferible ya que es más explícita y clara. | |
| 171 | + | ||
| 172 | + | Las matrices dinámicas se cuentan por referencia. Llamar a `setLength(miVaribleMatrizDinamica, 0)` en la práctica es equivalente a [[Nil|miVaribleMatrizDinamica := nil]] y disminuye el recuento de referencias. Al llegar el recuento de referencias a cero, se libera el bloque de memoria que ocupa la matriz. | |
| 173 | + | ||
| 174 | + | ``` pascal | |
| 175 | + | program VaribleMatrizDinamicaNilDemo(input, output, stdErr); | |
| 176 | + | var | |
| 177 | + | algo, tasca: array of char; | |
| 178 | + | begin | |
| 179 | + | setLength(algo, 1); | |
| 180 | + | algo[0] := 'X'; | |
| 181 | + | // copia refencia, se incrementa la cuenta | |
| 182 | + | tasca := algo; | |
| 183 | + | // a algo toma el valor "nil", el recuento de referencias disminuye | |
| 184 | + | setLength(algo, 0); | |
| 185 | + | writeLn('length(algo) = ', length(algo), | |
| 186 | + | '; length(tasca) = ', length(tasca)); | |
| 187 | + | ||
| 188 | + | // el recuento vuelve a decrecer | |
| 189 | + | tasca := nil; | |
| 190 | + | writeLn('length(algo) = ', length(algo), | |
| 191 | + | '; length(tasca) = ', length(tasca)); | |
| 192 | + | end. | |
| 193 | + | ``` | |
| 194 | + | ||
| 195 | + | Las matrices dinámicas se finalizan automáticamente. No es necesario hacer explícitamente `setLength(..., 0)` en todas las referencias cuando el programa llega a su fin, o cuando sale de un alcance en general. | |
| 196 | + | ||
| 197 | + | Sin `{$rangeChecks on}` es posible llegar más allá de los límites de una matriz. Eso significa que al iterar sobre matrices dinámicas, es imposible trabajar sin [`low`](https://www.freepascal.org/docs-html/rtl/system/low.html) y [`high`](https://www.freepascal.org/docs-html/rtl/system/high.html) para determinar índices válidos (el primero es opcional, ya que las matrices dinámicas siempre comienzan en cero ). Alternativamente, se pueden usar [[for-in loop|for … in loops]], si no se necesita el índice. | |
| 198 | + | ||
| 199 | + | Recordemos que, [[SizeOf|sizeOf]] de una matriz dinámica se evalúa como el tamaño de un puntero. | |
| 200 | + | ||
| 201 | + | ## Definición | |
| 202 | + | ||
| 203 | + | - Está en [`system.tBoundArray`](https://www.freepascal.org/docs-html/rtl/system/tboundarray.html) y [`objPas.tBoundArray`](https://www.freepascal.org/docs-html/rtl/objpas/tboundarray.html) | |
| 204 | + | ||
| 205 | + | ## Ver además | |
| 206 | + | ||
| 207 | + | - [[String|string cadena de texto]] | |
| 208 | + | - Ejemplo: [[Example: Multidimensional dynamic array|Matriz dinámica multidimensional]] | |
| 209 | + | - [Guía de referencia de Free Pascal: “Matrices dinámicas”](https://www.freepascal.org/docs-html/ref/refsu14.html#x38-510003.3.1) | |
| 210 | + | ||
| 211 | + | --- | |
| 212 | + | *From the Free Pascal wiki, page "Dynamic array/es", as it stood on 2022-05-11 after a change by Iskraelectrica, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).* | |
| 213 | + | Category: Code |
