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

jueves, 26 de diciembre de 2024

Lenguajes. R.

Vectorización de una matriz

Al igual que sucede con los vectores, también podemos vectorizar las operaciones matemáticas a los componentes de una matriz: al aplicar esa operación a la matriz, se aplica a cada uno de sus elementos, con independencia de la posición fila-columna que ocupen.



Además la vectorización también se aplica a los operadores relacionales, y como en el caso de los vectores, se trata de una operación que no altera la composición original de la matriz, por lo que si nos interesa conservar el resultado de la vectorización para operar después con él, deberemos asociarlo a una nueva matriz, resultante de la vectorización de la primera. 

Veamos este procedimiento sobre un ejemplo, partiendo de dos vectores...
  • alumnos <- c(24,22,20,19,21,25)
  • mataprob <- c(67,72,84,53,82,79)
...convertidos en matriz (cole <- rbind(alumnos,mataprob)que vectorizamos mediante un operador lógico (cole < 30), operación que asignamos a una nueva matriz (cole2 <-cole < 30). Observa el resultado de visualizar ambas matrices (cole y cole2) en la consola:


Como ves, mientras que cole conserva los valores iniciales, cole2 contiene el resultado de la vectorización: gracias a esta operación disponemos de una nueva matriz que recoge el resultado de aplicarla a cada uno de los datos de la matriz original.

Pero, ¿qué pasa si realizamos una vectorización (por ejemplo, aplicando ese mismo operador relacional) a una matriz que contiene datos alfanuméricos?. Si te fijaste en la imagen de matrices de la [entrada anterior], matriz resultante de la inclusión de tres vectores, el primero de los cuales estaba compuesto de datos alfanuméricos, todos sus datos habían sido transformados en datos alfanuméricos (de ahí que aparezcan entrecomillados).

Pues bien, si realizamos esta vectorización, el resultado es cuanto menos sorprendente, ya que devuelve FALSE para los datos alfanuméricos (identificador de curso) y TRUE o FALSE, según corresponde, en los numéricos primitivos  convertidos en alfanuméricos. Pero si la vectorización consiste en realizar una operación matemática (vg. cole2 <-cole * 30) el resultado es un mensaje de error, esperable por otra parte, ya que no puede realizar esa operación con los datos no-numéricos. La diferencia entre ambas operaciones es que no se ejecuta la vectorización con ninguno de los datos dado que ninguno es ya numérico.





Lenguajes.R.

Matriz y array

El hecho de que ambos sean conocidos como vectores multidimensionales ya dice mucho del peso del concepto vector en R. Ambas (matriz y array) comparte con el vector ser colecciones de datos homogéneos, esto es: de un mismo tipo, pero si diferencia en que su número de dimensiones no se limita a una.


Una matriz es un vector bidimensional (longitud y altura, podemos decir), por lo que se puede representar como una tabla de doble entrada, en la que la longitud son las columnas y la altura las filas.

Un array es un vector de más de dos dimensiones (n.dimensional, si se prefiere), por lo que se puede representar como un cubo , un hipercubo u otras formas más complejas. Es precisamente esta complejidad la que hace que el array sea una colección de datos poco usada en R, motivo por el cual en esta entrada me limitaré a hablar de la matriz como colección o estructura de datos.

Para crear una matriz empleamos la función matrix(), la cual acepta dos argumentos (nrow y ncol) (1) y los valores de ambos debe ser igual al número de elementos de la matriz, en caso contrario, obtendremos una matriz con datos truncados o con datos añadidos, siendo ésta una limitación a tener en cuenta (2).

Si queremos crear una matriz con unos contenidos específicos (esto es: no derivada de una serie de números (matrix(1:10)), deberemos generar una expresión de cierta complejidad (matrix(c("P1","P2","P3","P4","P5","P6"), nrow = 3, ncol = 2)) que además respete la condición antes indicada en cuanto a la relación entre el número de elementos y al producto de filas * columnas.

También podemos crear una matriz a partir de cierto número de vectores mediante las funciones cbind() y rbind()cbind() trata cada vector como una columna y rbind() como una fila.

Pongamos un ejemplo de conversión de vectores en matrices. Supongamos tres vectores, el primero con el identificador del curso, el segundo con el número de alumnos del grupo y el tercero con el porcentaje de aprobados en Matemáticas:
  • curso<-c("P1","P2","P3","P4","P5","P6")
  • alumnos <- c(24,22,20,19,21,25)
  • mataprob <- c(67,72,84,53,82,79)
Podemos construir una matriz vector-fila (cole <- rbind(curso,alumnos,mataprob)) o vector-columna (cole <- cbind(curso,alumnos,mataprob)) con los resultados que se muestran en la imagen siguiente (3):


Otras funciones que pueden sernos de utilidad para trabajar con matrices son la función class(), que nos permite saber si una colección de datos es considera una matriz (o un array) por R, la función dim(), que nos devuelve el número de filas y columnas de la matriz (o del array) y la función t(), que nos permite trasponer filas-columnas.

NOTAS

(1) Podemos crear una matriz sin estos argumentos, lo que deriva en crear una matriz unidimensional, esto es, el equivalente a un vector.
(2) Si este producto no es igual al número de elementos o bien se omiten los elementos que sobran o se reciclan los que faltan hasta completar las posiciones definidas en filas-columnas.
(3) En este caso, los tres vectores tienen la misma longitud, por lo que la estructura matricial se ajusta al contenido, pero si tuvieran diferente longitud, tendríamos las mismas limitaciones que vimos antes. Una solución para evitar elementos faltantes o duplicados es introduciendo valores NA en el vector que los precise hasta completar el número de elementos necesarios para que se ajusten las longitudes de los vectores constituyentes de la matriz, o los datos de ésta.

martes, 7 de mayo de 2024

Interfaz. OOo Basic

Campo de elección


Último campo que vamos a tratar en esta serie de entradas. Es un tipo de campo que normalmente se resuelve como campo simple, solicitando la entrada de datos al usuario, pero que presenta como peculiaridad que en ciertas circunstancias disponemos de información previa que podemos tener recogida como listado o base de datos. Es el caso de los listados de alumnos con sus datos básicos, por ejemplo: NIE, nombre, apellidos y fecha de nacimiento (1).


Tanto en el informe como en el dictamen nos encontramos con campos aparentemente sencillos e inevitablemente considerados de entrada directa y simple de datos. Un ejemplo (aunque no el único) es el que representa esta imagen: Dictamen de escolarización, datos personales del alumno (2)


En este caso, y a modo de ejemplo, me limitaré a los campos de esta tabla del dictamen de la que he eliminado el campo Edad ya tratado en la [entrada anterior]. Se trata de los campos NIE, Nombre y apellidos del alumno y Fecha de nacimiento y voy a presuponer que ya disponemos de información sobre esos datos puesto que contamos con una sencilla base de datos creada en Calc (3).


Como en el resto de los campos, al tratarse de un documento prescriptivo (dictamen) crearemos previamente los marcadores necesarios (3 marcadores) que usaremos en el procedimiento de escritura del documento que, en esta ocasión, resolveremos mediante bucle que llama a una función específica de posicionamiento (o escritura)

For i = 0 To 2:
sVar = mDatos(i)
Posicionamiento(sVar,"mcd",i)
Next
... habiendo asociado los contenidos (los datos a incluir en los campos del documento) a una matriz de datos (mDatos()) (4) la cual cumplimentaremos mediante la asociación de sus componentes a funciones...

mDatos(0) = NIE(CodAl)

... aunque por seguridad demos opción a modificar o corregir los datos que esa función devuelve en función del dato que le pasamos como parámetro (NIE(CodAl))

mDatos(0) = InputBox("NIE:","ALUMNADO", mDatos(0))

En este planteamiento, en el script principal es fundamental el papel que juega el procedimiento de obtención de la variable parámetro que se incluye en la llamada a las funciones (que veremos después). En esta ocasión he utilizado un método simple basado en InputBox():

CodAl = InputBox("Introduce el código de identificación del alumno: " & Chr(13) &_
"1. Roberto Marcial Pérez" & Chr(13) &_
"2. María Martínez Sanz" & Chr(13) &_
"3. Adela Decastro Morán" & Chr(13) &_
"4. Antonio Heernández Jiménez" & Chr(13) &_
"5. Matias Luján López" & Chr(13) &_
"6. Amalia Desantos Melquiades" & Chr(13), "ALUMNADO","1-2-3-4-5-6")

Este código nos permite asignar a la variable-parámetro CodAl el valor de posición que ocupan los datos de cada alumno en cada una de las matrices que se explicitan en las funciones de las cuales CodAl es parámetro, previa corrección de su valor (CodAl = CodAl-1) para asegurar la coincidencia (5).

Queda ahora analizar el funcionamiento de las funciones que devuelven esos datos (6) y como todas ellas (NIE(), Nombres(), Apellidos() y FechaNacimiento()) son iguales en su funcionamiento, me limitaré a explicar una de ellas como ejemplo.

La función NIE() se define con un parámetro (ID As Integer) y de tipo String (esto es: devuelve un dato texto) (7).

Dentro de la función se declara una matriz (Dim mNIE(5) As String), una variable (Dim AlNIE As String) y una variable contador (Dim i As Integer). A la primera se le asignan los datos de los NIE conocidos (v.g. mNIE(0) = "123456") y la variable nos sirve para hacer más claro el procedimiento de selección y la devolución de valor pertinente asignándola el valor correspondiente de la matriz anterior (AlNIE = mNIE(i)) y, posteriormente, asignando su contenido a la función (NIE = AlNIE).

Resulta clave comprender el procedimiento que permite ejecutar la búsqueda e identificación del valor de interés que se resuelve mediante este código. Muestro y explico:

For i = LBound(mNIE()) To UBound(mNIE()):
mNIE(i)
If i = Id Then
AlNIE = mNIE(i)
End If
Next
  • Un bucle For recorre la matriz de datos NIE asociando su índice con el valor de la variable contador i.
  • Se condiciona (If) la asociación del contenido del NIE que se desea localizar al cumplimiento de la condición  i = Id, siendo id (como sabemos) el parámetro que traslada a la función el valor seleccionado por el usuario en el script principal.
  • En el momento en que la condición i = Id -> True, entonces se realiza la asociación del valor de la matriz a la variable (AlNIE) que devuelve el resultado de la función.
Finalizo esta entrada [aportando un documento] que contiene el código explicado en ella (8) con el mismo objetivo de otras veces: facilitar su comprensión y las propuestas de mejora que se quieran aportar. Me planteo, no obstante, retomar este procedimiento ya que es mejorable desde dos punto de vista: el uso de las matrices y el acceso a la base de datos que contiene la información que aquí manejo mediante las funciones y las matrices que contienen.

NOTAS

(1) Podemos ampliar este listado de campos mucho más, incluyendo datos familiares y escolares. Sería de desear que se trabajar más con bases de datos (con independencia del soporte), tanto para registro como para consulta, pero no suele ser lo habitual en el ámbito de los SEO. Por desgracia y sin una razón clara, a parte del poco conocimiento de estas herramientas, es una práctica poco frecuente.
(2) Del mismo tipo son los datos familiares del dictamen y del informe. Tanto unos (los personales del alumno) como otros (los familiares) pueden ser conocidos a priori, como resultado del análisis del expediente del alumno o de la información aportada por otros servicios (en caso de nuevas escolarizaciones). Sería deseable que así fuera, como también que el SEO tuviera actualizadas sus bases de datos sobre esta información y sobre otras, pero...
(3) De no contar con estos datos ya elaborados, deberíamos aplicar a estos campos el procedimiento básico de solicitud de información al usuario del docap mediante InputBox(). Incluso en caso de nuevas escolarizaciones (EI 3 años) es posible contar con esta información en formato tabla Excel. De ella podemos partir para construir la base de datos que necesitamos. Existen, como sabemos, diferentes modo de acceder y utilizar estos datos. En esta entrada trataremos la forma más simple que se puede concretar en forma similar a como propusimos en [esta entrada]. Este será nuestro punto de partida.
(4) En el caso de nombre y apellidos he optado por emplear, como paso previo, dos variables (AlNombre y AlApellidos) para mejor manejo de los datos al estar estos diferenciados en la base de datos de origen, posteriormente se asignan mediante concatenación al elemento de la matriz (mDatos(1) = AlNombre & " " & AlApellidos)
(5) Recuerda que las matrices se inician con el elemento 0, no con el elemento 1
(6) Además contamos con una subrutina (Posicionamiento()) cuyo cometido es facilitar la escritura de los datos en la posición de los marcadores. No la explico ya que el funcionamiento de este código ha sido explicado en entradas anteriores, aunque integrado dentro del script principal. debido a ello, la única novedad es que aquí se emplea como subrutina y que cuenta con tres parámetros: dos para identificar el marcador y uno para recibir el dato o contenido a escribir (Posicionamiento (sDatos As String,MrN As String, i As Integer))  
(7) Dado que el NIE es en realidad un código de identificación, no es necesario considerarlo un valor numérico.
(8) Si en todos los casos este paso es conveniente, en este lo es especialmente. Aunque el documento cuenta con un comando que facilita el uso del script, el mero uso de código no es el objetivo, por lo que insisto en la necesidad de acceder al IDE y conocer el código de primera mano.

jueves, 2 de mayo de 2024

Interfaz. OOo Basic.

Campos condicionados


Otro tipo de campo frecuente en las tablas del dictamen y del informe es el que denomino aquí campo condicionado. Estos campos se caracterizan por ser dependientes de otro campo con el que se encuentra conceptualmente relacionado.


Veamos primero un ejemplo de este tipo de campos en el dictamen:


Los campos del apartado 5 del dictamen (también los del 6. Recursos de apoyo especializado) son de tipo condicionado, ya que el campo de la columna Sí/No condiciona su correspondiente de la columna Breve descripción: si contestamos en el primero deberemos incluir información en el segundo; en caso contrario el campo asociado quedará vacío (1)

También se presenta este tipo de campos en el modelo de informe, como comprobamos en la captura que sigue:


En este caso se trata de una tabla en la que se registra la presencia vs. ausencia de Medidas de Atención a la Diversidad (MAD). Su funcionamiento es igual al de la tabla anterior (dictamen): si la respuesta al ítem es , se deberá aportar información en el campo asociado de la columna 4. En caso contrario (No) su campo asociado quedará vacío.

Paso ahora a explicar el código necesario. Como ejemplo trabajaré con la tabla 5 del dictamen desarrollando una de las dos opciones, la menos eficiente, por lo que recomiendo una lectura atenta de todo lo que sigue.

Sub DictamenMod2

Dim oMarcador As Object
Dim sAjustesOrganizativosA As String, sAjustesOrganizativosB As String

sAjustesOrganizativosA = InputBox("¿Son necesarios ajustes organizativos y metodológicos?","DICTAMEN. 5. Adapciones que precisa","Sí-No")

 If sAjustesOrganizativosA= "Sí" Then

sAjustesOrganizativosB = InputBox("Describe las medidas organizativas, metodolóogicas y curriculares de acceso que consideras necesario.","DICTAMEN. 5. Adaptaciones que precisa")

ElseIf  sAjustesOrganizativosA= "Sí" Then

 sAjustesOrganizativosB = ""

Else

MsgBox("No has dado respuesta a lo demandado en el campo Ajustes organizativos del aparado 5 del DICTAMEN o la respuesta no se acepta como válida") 

   End If 

oMarcador = ThisComponent.getBookmarks().getByName("mcr0")
oMarcador.getAnchor.setString(sAjustesOrganizativosA)
oMarcador = ThisComponent.getBookmarks().getByName("mcr1")
oMarcador.getAnchor.setString(sAjustesOrganizativosB)

End Sub

  • Necesitamos crear la misma estructura que usamos en el caso de campos simples, incluyendo la creación de marcadores en el documento. 
  • Además de la variable object (oMarcador) que nos permite acceder a los objetos marcador del documento (mcr0 mcr1) Declaramos dos variables de contenido por aspecto: una para el campo condicionante (sAjustesOrganizativosA ) y otra para el condicionado (sAjustesOrganizativosB):
Dim oMarcador As Object
Dim sAjustesOrganizativosA As String, sAjustesOrganizativosB As String
  • Dar contenido a la variable condicionante se ajusta al modelo de campo básico, por lo que se le puede tratar mediante la misma fórmula...
sAjustesOrganizativosA = InputBox("¿Son necesarios ajustes organizativos y metodológicos?","DICTAMEN. 5. Adapciones que precisa","Sí-No")

... pero esto genera un posible error por omisión (respuesta vacía) o por separarse (en forma o en contenido) de las dos únicas opciones válidas (Sí/No). Para controlar estos posibles errores se pueden usar diferentes opciones y aunque estos errores sean excepcionales, su solución no es simple (2).

Otra posible formulación pasa por emplear la función MsgBox(), lo que nos obliga, a su vez una nueva variable Integer (Dim iR as Integer) a asociar a la función MsgBox(). Esta opción evita posibles errores por parte del usuario, aunque obliga también a modificar la estructura condicional, condicionando un script alternativo:

 Dim oMarcador As Object

Dim sAjustesOrganizativosA As String, sAjustesOrganizativosB As String

Dim iR As Integer

iR = MsgBox ("¿Son necesarios ajustes organizativos y metodológicos?",4,"DICTAMEN. 5. Adaptaciones que precisa")

 If iR = 6 Then

sAjustesOrganizativosA = "Sí"
sAjustesOrganizativosB = InputBox("Describe las medidas organizativas, metodolóogicas y curriculares de acceso que consideras necesario.","DICTAMEN. 5. Adapciones que precisa")

Else

 sAjustesOrganizativosA = "No"
 sAjustesOrganizativosB = ""

 End If 

Como podemos comprobar, esta segunda opción simplifica el script y lo hace más resisten a errores de uso (en realidad los reduce a cero), por lo que es preferible al primer script (3).

  • Finalmente, la escritura en el texto se mantiene en la formulación ya explicada en la entrada anterior sobre campos simples, aunque ahora son necesarias dos fases de escritura, que aquí se resuelven meramente por repetición del mismo procedimiento (4):

oMarcador = ThisComponent.getBookmarks().getByName("mcr0")
oMarcador.getAnchor.setString(sAjustesOrganizativosA)
oMarcador = ThisComponent.getBookmarks().getByName("mcr1")
oMarcador.getAnchor.setString(sAjustesOrganizativosB)

NOTAS

(1) Este segundo campo también sería condicionado si el contenido a introducir fuera uno u otro según la respuesta dada al campo condicionante. El ejemplo que presento aquí es la formulación más simple de este tipo de campos, pero también el único que se presenta en el informe y en el dictamen.

(2) En este caso se incluye en InputBox() las dos opciones de respuesta como tercer parámetro de la función; también se incluye la opción Else en el condicional If con un mensaje de advertencia. En el modelo propuesto podría añadirse a continuación de Else volver a interrogar sobre la variable sAjustesOrganizativosA y repetir el análisis que realiza el condicional, dando así una segunda opción. Como se puede ver, la solución no es sencilla, aunque en realidad es posible que estas circunstancias adversas se produzcan excepcionalmente.

(3) En principio, cuando nos encontremos con un campo de dos opciones siempre es preferible utilizar la función MsgBox() en lugar de InputBox(); no ahorraremos posibles errores y obtendremos un código más limpio y funcional. MsgBox() funciona en realidad como Boolean, pero su configuración de respuesta en OOo Basic nos obliga a asociarla a una variable numérica, ya que sus comandos (aquí el parámetro 4, que muestra las opciones - No) devuelven respectivamente los valores 6 y 7.

(4) Cuando el número de variables y marcadores a usar se multiplique será necesario trabajar con una matriz para agrupar las variables y un bucle para el proceso de escritura. Para los objetivos de esta entrada ambos resultan innecesarios. 

lunes, 25 de marzo de 2024

Textos. OOo Basic

Trabajar con listas.

Uno de los tipos específicos de texto que tenemos que crear con cierta frecuencia  son los listados, bien como documentos en si mismos, bien como elementos de un documento más amplio. Para hacerlo mediante teclado (que suele ser lo común) existen tres opciones básicas: crear la lista desde cero, copiar y pegar los ítem recurriendo a un documento externo que contiene la lista maestra o utilizar esa lista maestra como plantilla y, al contrario que en los casos anteriores, eliminar los ítem que no interesen. Aunque la tercera fórmula es el modo de trabajo que más se acerca al objetivo de la automatización, todas ellas, en realidad, están muy lejos que acercarse a este objetivo. Usar macros y script (OOo Basic) es la única forma en que realmente se facilitar la tarea.


Aunque no siempre es posible ni conveniente utilizar macros y/o script para automatizar la creación de un listado (1), hay razones para pensar que son muchas las circunstancias en las que sí merece la pena crear procedimientos informatizados para ayudarnos en la tarea. esto es especialmente cierto cuando contamos con un listado previo y relativamente estable y más aun si ese listado es de cierta extensión. Ejemplos hay muchos: listado del alumnado con NEAE, relato de los asistentes a una reunión de la UO (2), listado de indicadores de evaluación a incluir en una PTI...

En cualquier caso, y para lo que ahora nos importa, disponer de procedimientos de generación informatizada de listados nos va a ayudar a simplificar el trabajo, aunque requiera uno previo.

No me voy a detener demasiado en el aspecto formal de la creación de un listado, que podemos resolver mediante el uso de tablas o mediante la activación de las marcas del ítem de lista (vg. un número índice o una bolita) por medio de una macro. Pero tampoco quiere dejar de explicar cómo hacerlo, así que dedicaremos el inicio de esta entrada a explicar esta forma de trabajo mediante macros simples y script.

Como sabemos, las listas son de dos tipos genéricos: alfanuméricas y gráficas. Las dos macros simples que las generan son las siguientes:

 Alfanumérica:

 dim args1(0) as new com.sun.star.beans.PropertyValue
args1(0).Name = "On"
args1(0).Value = true
dispatcher.executeDispatch(document, ".uno:DefaultNumbering", "", 0, args1())

Gráfica (en este caso de bolita)

dim args1(0) as new com.sun.star.beans.PropertyValue
args1(0).Name = "On"
args1(0).Value = Valor
dispatcher.executeDispatch(document, ".uno:DefaultBullet", "", 0, args1())

Ambas trabajan con argumentos booleanos (arg().Value), así que la conversión de estas macros simples en subrutinas para ser empleadas desde un script, supone implementar un argumento (variable) de tipo Booleansub ListaSimple (Valor As Boolean) que deberemos incluir en la llamada a la subrutina como en este ejemplo: ListaSimple(True). Cuando queramos finalizar el listado, deberemos llamar de nuevo a la subrutina y cambiar el argumento a False.

Además de este código, también necesitamos el código (macro o script) de escritura y el salto de línea al finalizar el ítem... 

dispatcher.executeDispatch(document, ".uno:InsertPara", "", 0, Array())

Podemos anidar listas (e ítem) mediante el uso de tabulador, generando un incremento del sangrado que facilita la estructuración de la lista en ítem principal e ítem secundario. Para ellos haremos uso de la misma macro-subrutina de escritura, pasando como argumento el carácter especial Chr$(9). A continuación te muestro el script que nos permite generar una lista (los inicios de la misma) que contiene otra anidada. Hacerla más amplia y funcional es cosa de escribir más líneas de código:

Sub CrearLista

VarSis

ListaSimple(True)

Escribir("Listado de alumnos con NEE")

SaltarLinea

ListaSimple(False)

Escribir(Chr$(9))

ListaSimpleNum(True)

Escribir("Jaime Ortega López")

End Sub

... y este es el resultado. Simple por haberme ahorrado esfuerzos, pero suficiente para explicar lo que me interesa.

Con esto y con lo que sabemos sobre escritura y sobre interfaces podemos crear listas automatizando parte del proceso, aunque realmente lo más pesado del trabajo queda pendiente de automatizar. Un paso más hacia ese objetivo supone hacer uso de matrices, bucles e incluso condicionales. He aquí un ejemplo:

Sub Semana

VarSis
Dim DiasSemana(6) As String
Dim i As Integer


DiasSemana(0) = "Lunes"
DiasSemana(1) = "Martes"
DiasSemana(2) = "Miércoles"
DiasSemana(3) = "Jueves"
DiasSemana(4) = "Viernes"
DiasSemana(5) = "Sábado"
DiasSemana(6) = "Domingo"


ListaSimple(True)
Escribir("Días de la semana")
SaltarLinea
ListaSimple(False)
Escribir(Chr$(9))
ListaSimpleNum(True)


For i = LBound(DiasSemana()) To UBound(DiasSemana())
Escribir(DiasSemana(i))
If i < UBound(DiasSemana()) Then
SaltarLinea
End If
Next

End Sub


Como puedes observar, este script se divide en cuatro partes:

  • En la primera se llama a VarSis que nos permite trabajar con macros y se declara una matriz (DiasSemana(6)) y una variable contador (i)
  • En la segunda se asignan valores a cada elemento de la matriz
  • En la tercera se llama a un conjunto de macros para escribir la primera línea del código, saltar a la línea siguiente, generar un  espacio de tabulación e implementar el formato numérico de la lista (3)
  • La cuarta parte es la de mayor complejidad y novedad. En síntesis recorremos la matriz con un bucle For utilizando como referencias los valores inicial y final de la estructura de la matriz mediante las funciones LBound() y UBound() y llamamos a la macro de escritura asignando como parámetro cada un de los elementos de la matriz. El condicional If evita que se escriba un elemento de la lista innecesario, si bien su función inmediata es escribir los elementos numerales del listado mediante la macro SaltarLinea.

Muestro a continuación el resultado. No parece gran cosa, pero que supone la escritura automática de una lista:

 

Nos falta dar un paso más que básicamente consiste en dar la posibilidad de automatizar la escritura de una lista resultante de la selección de ítem de otra lista. Viene a ser el equivalente a trabajar sobre un documento-base que contiene a lista completa de la que vamos borrando los elementos que no nos interesan. De este modo conseguimos generar listas personalizadas, con todos los posibles usos prácticos que podemos intuir (4)

Este es el script. Por simplicidad sustituyo la escritura en el documento por el mero visionado de los elementos de la lista mediante MsgBox, pero el principio de funcionamiento del script no supone una variación sustancial. Esta simplificación permite que nos centremos en lo fundamental del funcionamiento del script.

Sub MatrizSemana

Dim DiasSemana(6) As String
Dim MisDias () As String 
Dim Dia As Integer
Dim i As Integer, b As Integer


DiasSemana(0) = "Lunes"
DiasSemana(1) = "Martes"
DiasSemana(2) = "Miércoles"
DiasSemana(3) = "Jueves"
DiasSemana(4) = "Viernes"
DiasSemana(5) = "Sabado"
DiasSemana(6) = "Domingo"


b = 0
For i = LBound(DiasSemana()) To UBound(DiasSemana())
Dia = MsgBox (DiasSemana(i),4,"MIS DIAS")
If Dia = 6 Then
ReDim Preserve MisDias(b)
MisDias(b) = DiasSemana(i)
b=b+1
End If
Next

MsgBox "Número de días seleccionados: " & CStr(UBound(MisDias()) + 1)

For i = LBound(MisDias()) To UBound(MisDias())
MsgBox MisDias(i)
Next

End Sub

Como puedes ver es similar al script anterior, aunque con una diferencia fundamental: ahora se trata de "pasar los datos de la matriz original a "nuestra" matriz (la que nos interesa) seleccionando de la primera los ítem que nos resultan de interés. Este proceso de traslado de datos tiene cierta complejidad, por lo que requiere una explicación. Pero vayamos por partes. 

Apreciamos cuatro fases en el desarrollo del script (5), la primera de declaración de variables y matrices, la segunda de asignación de contenido a la matriz-origen de datos, la tercera de selección de contenidos para generar la matriz de destino (la "personalizada") y la cuarta para la salida de datos de esa matriz-destino.

A diferencia del modelo anterior, en la primera parte del script declaramos dos matrices (DiasSemana(6) MisDias ()), cuyos nombres son claramente explicativos de su función. Contiene además una variable-puente para pasar los datos de una matriz a otra (Dia) y dos contadores (i y b): el primero es empleado en el funcionamiento de los bucles y el segundo será explicado en su momento (6).

La segunda parte es idéntica a la equivalente en el script anterior, pero la tercera (primer bucle For) supone un cambio fundamental respecto a ese script: 
  • El contenido de la variable de paso Dia se asocia a la función MsgBox() para que el usuario seleccione (comandos /No) los ítem que le interesen de la matriz principal, la cual se usa como información en el cuerpo del MsgBox: 
Dia = MsgBox (DiasSemana(i),4,"MIS DIAS")

  •  El condicional que sigue permite condicionar la incorporación del dato en función de la respuesta anterior (). De no cumplirse esa condición no se produce el paso del valor a la matriz-destino: MisDias(b) = DiasSemana(i)
  • Pero, además, en el cuerpo de ese condicional, y antes de la asignación antes señalada, redimensionamos la matriz-destino, manteniendo su contenido (ReDim Preserve MisDias(b)). Esto es fundamental para controlar que la matriz-destino no incluya elementos no deseados (posiciones vacías).
  • Nos debemos fijar ahora en el uso del contador b: hemos asignado un valor inicial antes del inicio del bucle (b = 0), lo utilizamos como índice de la matriz de destino en sustitución del contador i que utilizamos como índice en la matriz-origen, tanto para el redimensionado como en la asignación, y finalmente y también dentro del condicional, incrementamos su valor como contador (b=b+1).
  • El resultado de esta estructura es que el usuario selecciona/rechaza cada uno de los ítem de la matriz-origen (7), pero sólo se incorpora a la matriz-destino el ítem seleccionado. El MsgBox que sigue permite comprobar el número de elementos de la matriz-destino una vez finalizado el proceso de selección (MsgBox "Número de días seleccionados: " & CStr(UBound(MisDias()) + 1)) (8)
La cuarta parte (y final) del script es en realidad muy simple: un segundo bucle recorre ahora la matriz-final mostrando su contenido mediante un MsgBox. Si en el primer bucle el uso de las funciones LBound() y UBound() puede considerar opcional (ya que conocemos las dimensiones de la matriz-origen), en este segundo bucle es prescriptivo, ya que, por definición ignoramos las dimensiones de la matriz-destino.

Para finalizar, y por resumir, en esta entrada hemos aprendido a crear listados automatizando su escritura pero, lo que es más interesante, su contenido. Este uso del código nos puede ser de mucha utilidad, aunque requiere crear la o las matrices originales y recorrerlas en su integridad, si bien existe la posibilidad (aquí no contemplado) de salir del bucle de selección una vez satisfechos los criterios de selección que tengamos en mente.

NOTAS.

(1) No veo la posibilidad ni la conveniencia de hacerlo cuando la lista es sencilla y no partimos de una definición clara de los ítem. En esos casos el trabajo de crear el procedimiento informatizado puede ser mayor que el que haremos escribiéndola directamente.
(2) Este en realidad es un ejemplo dudoso, ya que se puede incluir el listado completo en la plantilla del acta de la RUO y proceder simplemente suprimiendo los no asistentes. No obstante sí puede ser de interés automatizar la elaboración del acta, con lo que la inclusión del listado de asistentes será parte del proceso, aunque no necesariamente si partimos de un documento-modelo que contenga la lista de componentes de la UO.
(3) Todo ello reproduce el modelo de uso explicado al inicio de esta entrada.
(4) La complejidad del contenido depende especialmente de los contenidos de la propia lista: desde una sencilla y de pocos elementos, hasta una de muchos componentes, como puede ser el listado de las pautas DUA o de los criterios de evaluación de un área curricular.
(5) Eliminar la escritura en el documento permite esta reducción del contenido del script. En otro caso sería necesario, cuanto menos, incluir una llamada a la subrutina de escritura y si este proceso lo realizáramos empleando macros y manteniendo la visualización que hemos generado en las formas anteriores, implicaría el añadido de los elementos que utilizamos en el script anterior.
(6) En este script no se llama a VarSis por no ser necesario para el funcionamiento esperado. De querer utilizar macros para escribir en el documento, deberíamos incluirlo.
(7) El procedimiento de ir seleccionado ítem por ítem hace que sea un tanto lento, y más aun si el número de ítem es elevado. Es posible salir de bucle mediante la instrucción Exit asociada a un condicional, y también es posible que existan formas de condicionar la presentación o no de determinados ítem de la matriz-origen. La primera fórmula no la uso en este caso y la segunda, en este momento desconozco cómo implementarla.
(8) Dado que es ésta su única función es perfectamente posible prescindir de él en la formulación final del script, pero es muy útil durante la fase de pruebas, motivo por el cual lo he mantenido. Obsérvese que hago uso de la función UBound(MisDias()) para conocer el valor de posición del último elemento de la matriz-destino, añadiendo +1 para que el resultado sea directamente comprensible (el número exacto de elementos de la matriz-destino). Esto es debido a que, como sabemos, el primer elemento de una matriz se identifica como 0.