Borrador distinto al examen final

Un patrón de software que ha incrementado su utilidad y popularidad, en especial con la inteligencia artificial, es el pipeline de tareas. En lugar de crear clases enormes que realizan múltiples tareas con sus métodos, se crean clases más pequeñas que realizan una única tarea, llamadas agentes. Estas clases se encadenan para formar una secuencia o cadena de procesamiento de datos (en inglés, pipeline), donde la salida de un agente es la entrada del siguiente agente en la cadena. Tiene entre muchas ventajas, que rápidamente se pueden crear nuevos pipelines reutilizando agentes existentes ya probados.

Por ejemplo, si se quiere saber si una dirección de correo electrónico provista por un usuario es académica, se puede construir un pipeline con un agente que convierta texto a minúsculas, otro que extraiga sus dos partes (usuario y dominio), y finalmente uno que verifique si el dominio (ej.: ucr.ac.cr) es académico. Otro ejemplo es un pipeline que determina si un número telefónico en Costa Rica es de una línea fija o móvil. Se puede encadenar un primer agente que verificaría si el número contiene la cantidad correcta de dígitos, seguido por otro agente que revisa el primer dígito para determinar si es fijo o móvil.

Los datos que fluyen por un pipeline se le llaman tarea. Inicialmente la tarea está incompleta. Conforme avanza por el pipeline se va transformando hasta salir del pipeline como una tarea completa. En los ejemplos anteriores hubo dos tareas: determinar si un correo electrónico es académico y determinar si un número de teléfono es fijo o móvil.

Ejemplo 1 Ejemplo 2

Entrada:

email
3 1 2 3

Rodrigo.Chavez.CR
LUIS.ROJAS@ucr.ac.cr
academica@gmail.com
AnaSOTO34@mit.edu

Entrada:

phone
3 2 1 2

22229000
64098861
00007777

Salida:

0 {} addr=Rodrigo.Chavez.CR user= domain=
1 {} addr=rodrigo.chavez.cr user= domain=
2 {invalid} addr=rodrigo.chavez.cr user= domain=
3 {invalid} addr=rodrigo.chavez.cr user= domain=

0 {} addr=LUIS.ROJAS@ucr.ac.cr user= domain=
1 {} addr=luis.rojas@ucr.ac.cr user= domain=
2 {valid} addr=luis.rojas@ucr.ac.cr user:luis.rojas domain=ucr.ac.cr
3 {academic valid} addr=luis.rojas@ucr.ac.cr user:luis.rojas domain=ucr.ac.cr

0 {} addr=academia@gmail.com user= domain=
1 {} addr=academia@gmail.com user= domain=
2 {valid} addr=academia@gmail.com user=academia domain=gmail.com
3 {valid} addr=academia@gmail.com user=academia domain=gmail.com

0 {} addr=AnaSOTO34@mit.edu user= domain=
1 {} addr=anasoto34@mit.edu user= domain=
2 {valid} addr=anasoto34@mit.edu user=anasoto34 domain=mit.edu
3 {academic valid} addr=anasoto34@mit.edu user=anasoto34 domain=mit.edu

Salida:

0 {} 22229000
2 {} 22229000
1 {valid} 22229000
2 {landline valid} 22229000

0 {} 64098861
2 {} 64098861
1 {valid} 64098861
2 {mobile valid} 64098861

0 {} 00007777
2 {} 00007777
1 {} 00007777
2 {} 00007777

Cuando la tarea pasa por un agente, éste puede guardar información adicional en la tarea que será de utilidad para el siguiente agente o para el usuario final que solicitó la tarea. Además de los datos iniciales (por ejemplo, el correo electrónico o el número de teléfono), la tarea puede tener un conjunto de etiquetas adicionales. Cada agente puede consultar, agregar o quitar etiquetas a la tarea. Por ejemplo, el agente que verifica si un número de teléfono tiene la cantidad correcta de dígitos puede agregar una etiqueta valid a la tarea. El agente que determina si el número es fijo o móvil agregará la etiqueta landline o mobile si encuentra la etiqueta valid en la tarea, de lo contrario, no agregará ninguna etiqueta.

Elabore un programa en C++ que implemente pipelines de tareas. En la primera línea de la entrada estándar recibirá el tipo de tarea a procesar. La palabra email indica que se quiere determinar si los correos electrónicos son académicos. La tarea phone para determinar si números telefónicos son fijos o móviles.

La segunda línea de la entrada estándar construye el pipeline. El primer número indica la cantidad de agentes que conformarán el pipeline. En ambos ejemplos de entrada se solicitaron pipelines compuestos de tres agentes. En la misma línea se proveen tantos números enteros como agentes se indicaron en el primer número de la línea. Cada agente está identificado con el número que se indica en la siguiente tabla. Nótese que los agentes pueden repetirse en el pipeline. Puede suponer que siempre se proveen números identificadores válidos de agentes.

Correo electrónico

Números telefónicos

  1. Agente que convierte texto a minúsculas. No modifica las etiquetas de la tarea.

  2. Agente que separa una dirección de correo electrónico de la forma username@domain en usuario y dominio. Una dirección de correo es válida si tiene una única arroba (@), un nombre de usuario no vacío, y un dominio con al menos tres caracteres y uno de ellos es un punto (.). En caso de ser válida, guarda el nombre de usuario y el dominio en la tarea de correo electrónico y agrega la etiqueta valid. En caso contrario, agrega la etiqueta invalid a la tarea.

  3. Agente que verifica si el dominio de la dirección de correo electrónico termina en .edu o en .ac.xx donde xx puede ser cualquier texto no vacío. En caso afirmativo, agrega la etiqueta academic, en caso contrario, no agrega ninguna etiqueta.

  1. Agente que verifica que un número de teléfono tenga 8 dígitos. En tal caso agrega la etiqueta valid. En caso contrario, no agrega ninguna etiqueta.

  2. Agente que clasifica números telefónicos en líneas fijas o móviles. Si la tarea no tiene la etiqueta valid, no modifica la tarea y la pasa al siguiente agente. Si la tarea tiene la etiqueta valid, agrega la etiqueta mobile en caso de que el primer dígito del número telefónico sea 6, 7 u 8, de lo contrario agrega la etiqueta landline.

Después de una línea en blanco, se proveen en la entrada estándar tantas líneas como tareas se quieren procesar en el pipeline construido. Su programa puede leer una tarea a la vez y hacerla fluir por todos los agentes del pipeline. Para corroborar que cada tarea se procesa correctamente, su programa debe imprimir en la salida estándar la tarea recién leída, y cada vez que sale de un agente, usando el formato agent {tags} task, donde agent es el número de agente (0 cuando fue recién leída), {tags} es el conjunto de etiquetas que tiene asociada la tarea separadas por un espacio en blanco e impresas en orden, y task es la tarea en sí en función de sus campos.

Toda tarea tiene un conjunto de etiquetas textuales, inicialmente vacío. Puede suponer que una vez que se agrega una etiqueta, no se vuelve a eliminar. Si una etiqueta se agrega más de una vez, no debe repetirse en el conjunto. Utilice una colección de la biblioteca estándar de C++ apropiada para este comportamiento. Los demás campos de la tarea dependen de su tipo. Una tarea de correo electrónico tiene tres campos textuales: la dirección de correo electrónico leída, el nombre de usuario y el dominio. Una tarea de número telefónico tiene un único campo adicional: el número telefónico leído como un entero. Dado que las tareas son de diferente tipo de datos, use programación genérica para evitar redundancia de código. Consecuentemente, el algoritmo que las lee y las hace fluir por el pipeline debe ser también genérico capaz de trabajar con una cantidad arbitraria de tareas que podrían agregarse en el futuro.

Los agentes por su parte, aunque todos procesan tareas, lo hacen de muy diferente forma. Idee un mecanismo para imponer que nuevos agentes se apeguen a las imposiciones de su algoritmo de pipeline. Note que el pipeline compuesto de agentes se crea dinámicamente al leer la segunda línea de la entrada estándar. Su programa debe asociar estos números con los agentes correspondientes en un mecanismo de fábrica de agentes. Esta fábrica será el único lugar permitido donde puede implementar una "lógica de switch". Recuerde que su programa no debe generar fugas de memoria ni accesos inválidos.