Saltar al contenido principal

Secuencias

Un <sequence> declara una columna nombrada de valores, con una longitud de count. Piénselo como una columna terminada que existe antes de que empiece el renderizado: el generador llena todas las filas, y en cada fila de salida se toma un valor por su nombre, ya sea con interpolación ${{Name}} o dentro de una condición if.

Ese es el modelo lógico, y todo lo de esta página se deduce de él. Si la columna realmente vive en memoria es otra pregunta que el motor responde por usted: el motor por omisión calcula cada valor a partir de su número de fila mientras la fila pasa, y nunca guarda la columna, mientras que mode="memory" y la API de objetos sí construyen el arreglo. Los valores son los mismos en ambos casos — vea Salidas grandes.

Las secuencias viven en <env>, junto a count y seed. El diseño de una fila (en <block>) nunca genera valores por sí mismo: solo lee las columnas que sus secuencias ya produjeron.

Las salidas de ejemplo de abajo son ilustrativas: muestran la forma del resultado y pueden variar entre versiones del núcleo.

Seis filas de una ejecución real. Cada secuencia llena su propia columna; una fila es lo que se obtiene al leerlas de forma transversal.
  • Auna secuencia — una columna, que se llena de manera independiente
  • Buna segunda secuencia, que no sabe nada de la primera
  • Cuna tercera
  • Duna fila: la misma posición tomada de cada columna y ensamblada en una línea de salida

Atributos​

AtributoObligatorioQué hace
namesíEl nombre de la secuencia — con él se la referencia, p. ej. ${{City}}
parentnoRestringe esta secuencia a un subconjunto de filas. Sintaxis: Parent.Value
commentnoUna nota de texto libre; el motor la ignora

El único hijo obligatorio es una o más etiquetas <gen>, que producen los valores reales.

Una secuencia simple​

Se usa cuando hace falta una sola columna que se pueda referenciar por nombre desde cualquier parte del diseño. Se declara una vez en <env> y se lee con ${{City}}:

<tdc>
<env count="5" seed="demo">
<sequence name="City">
<gen type="text" value="Guadalajara,Monterrey,Puebla"/>
</sequence>
</env>
<block>
<line><data>${{City}}</data></line>
</block>
</tdc>
./run city.tdc (count=5, seed=demo)
Monterrey
Guadalajara
Puebla
Monterrey
Puebla

Por omisión los valores se eligen al azar de la lista, y el seed fija exactamente cuáles le tocan. ¿Quiere el orden estricto de la lista? Agregue order="sequential" — vea Máscaras y mayúsculas.

Una secuencia «simple» se define por tener exactamente un <gen> sin nombre. El acceso es ${{NombreDeLaSecuencia}}.

Con proporciones​

La misma forma de una sola columna cubre también las selecciones ponderadas. Dele al generador text una lista percent y los valores salen en proporciones exactas:

<sequence name="Gender">
<gen type="text" value="Hombre,Mujer" percent="60,40"/>
</sequence>

Con count=100 esto produce exactamente 60 Hombre y 40 Mujer: percent es un reparto exacto a lo largo de toda la ejecución, no una probabilidad por fila. Vea Determinismo y proporciones para saber cómo se hace valer percent.

./run gender.tdc (count=100)
Hombre   60
Mujer    40

Una secuencia compuesta por valor​

Úsela cuando un valor se arma a partir de varios: un nombre completo a partir del nombre, un espacio y el apellido. Deje los <gen> sin nombre y ponga el texto literal entre ellos:

<sequence name="FullName">
<gen type="template" value="person.male.firstName"/>
<data> </data>
<gen type="template" value="person.lastName"/>
</sequence>
./run people.tdc
Robert Williams
James Johnson
John Smith

${{FullName}} es un solo valor, así que entra tal cual en una ficha, en una columna CSV o en una de Parquet: no hay nada que unir en la salida.

Los <gen> con y sin nombre conviven en un mismo cuerpo. Los que no llevan nombre construyen el valor propio de la secuencia; el que sí lo lleva es un campo aparte, se lee como ${{Name.Field}} y no entra en la concatenación.

Una secuencia compuesta​

Se usa cuando una «entidad» tiene varios campos relacionados —nombre, apellido, edad— que van juntos. Agrúpelos en una sola secuencia en vez de tres separadas, y dele a cada <gen> un name:

<sequence name="Person">
<gen name="FirstName" type="template" value="person.male.firstName"/>
<gen name="LastName" type="template" value="person.lastName"/>
<gen name="Age" type="number" value="18..60"/>
</sequence>

A los campos se llega con un punto:

<line><data>${{Person.FirstName}} ${{Person.LastName}}, edad ${{Person.Age}}</data></line>
./run people.tdc (count=3, seed=demo, local=es)
Leandro Zambrano, edad 39
Leandro Juárez, edad 35
Rodolfo Miranda, edad 26

Un campo constante​

Un <data> con name es un campo constante y, a diferencia de un generador de un solo valor, no gasta una tirada:

<sequence name="Row">
<gen name="id" type="increment" value="1"/>
<data name="source">import-2026</data>
</sequence>

${{Row.source}} vale import-2026 en todos los registros.

Por qué no <gen type="text" value="import-2026"/>

Produce el mismo valor y aun así gasta una tirada por fila, así que meterlo en una configuración ya escrita desplaza todas las columnas declaradas después. Un <data> con nombre no gasta ninguna, y es la única forma de añadir una constante a una configuración que alguien ya está usando.

Reglas de las compuestas​

  • Un <gen> con name es un campo; sin nombre forma parte del valor propio de la secuencia (ver secuencia compuesta por valor arriba). Un cuerpo con todos los <gen> nombrados es una compuesta por campos y no tiene valor propio.
  • Los nombres de campo deben ser únicos dentro de la secuencia (error TDC111 si hay un duplicado).
  • Todos los campos comparten el mismo filtro parent. Si hay un padre definido, cada campo se llena solo en las filas que coinciden y queda vacío en el resto.
  • Referenciar una secuencia compuesta sin un campo —${{Person}}— deja el texto en la salida tal cual. Ese marcador intacto es la pista de que en realidad quería ${{Person.FieldName}}.

Campos en condiciones if​

El acceso con punto también funciona dentro de las condiciones, no solo en ${{...}}:

<line if="Person.Age >= 18"><data>adulto</data></line>
<line if="Person.Age < 18"><data>menor</data></line>

Una secuencia condicional​

Se usa cuando el valor de una columna depende de otra columna. Se enumeran varias ramas <gen if="…">; gana la primera rama cuya condición sea verdadera. Un <gen> sin nombre (sin if) actúa como el «si no»:

<sequence name="Age">
<gen type="number" value="0..90"/>
</sequence>

<sequence name="AgeGroup">
<gen if="Age < 18" type="text" value="Menor"/>
<gen if="Age < 65" type="text" value="Adulto"/>
<gen type="text" value="Mayor"/>
</sequence>

Como las ramas se prueban de arriba hacia abajo, Age < 65 solo llega a ver filas que ya fallaron Age < 18, así que en la práctica significa «18–64». El acceso es por el nombre de la secuencia, ${{AgeGroup}}:

./run agegroup.tdc
14  Menor
42  Adulto
71  Mayor
9   Menor
58  Adulto

Una secuencia condicional sigue siendo una sola columna: se lee como ${{AgeGroup}}, igual que una simple. Las varias etiquetas <gen> solo deciden qué valor llena cada fila.

Secuencias dependientes (parent)​

Una secuencia puede depender de otra con parent="Parent.Value". La hija se calcula solo en las filas donde el padre coincide, y cualquier porcentaje dentro de ella se mide contra ese subconjunto, no contra el count completo. Este es el corazón del modelo jerárquico de TDC:

<sequence name="Gender">
<gen type="text" value="Hombre,Mujer" percent="50,50"/>
</sequence>

<sequence name="MaleName" parent="Gender.Hombre">
<gen type="template" value="person.male.firstName"/>
</sequence>

<sequence name="FemaleName" parent="Gender.Mujer">
<gen type="template" value="person.female.firstName"/>
</sequence>

MaleName se materializa solo en las filas donde Gender == Hombre; en las demás queda vacía. El tratamiento completo —porcentajes anidados, varios niveles— está en Dependencias jerárquicas.

Una secuencia compuesta con parent​

Lo compuesto y parent se combinan directamente. Aquí cada género obtiene su propia entidad agrupada, y el subconjunto masculino lleva un campo Rank adicional:

<sequence name="Gender">
<gen type="text" value="Hombre,Mujer" percent="50,50"/>
</sequence>

<sequence name="Male" parent="Gender.Hombre">
<gen name="FirstName" type="template" value="person.male.firstName"/>
<gen name="LastName" type="template" value="person.lastName"/>
<gen name="Rank" type="text" value="Soldado,Capitán" percent="70,30"/>
</sequence>

<sequence name="Female" parent="Gender.Mujer">
<gen name="FirstName" type="template" value="person.female.firstName"/>
<gen name="LastName" type="template" value="person.lastName"/>
</sequence>

<block>
<line if="Gender == Hombre"><data>H: ${{Male.FirstName}} ${{Male.LastName}} (${{Male.Rank}})</data></line>
<line if="Gender == Mujer"><data>M: ${{Female.FirstName}} ${{Female.LastName}}</data></line>
</block>
./run ranks.tdc (count=6, seed=demo, local=es)
M: Teodora Cifuentes
H: Fermín Ordóñez (Soldado)
M: Andrea Guerra
M: Eugenia Mesa
H: Quintín Posada (Capitán)
H: Eleuterio Uribe (Soldado)

Lo que esto garantiza:

  • Los tres campos Male.* se materializan solo en las filas masculinas; en las femeninas quedan vacíos (y viceversa).
  • Male.Rank se reparte 70% Soldado, 30% Capitán dentro del subconjunto masculino: los porcentajes se cuentan contra el número de hombres, no contra el count total.

Determinismo y orden de los campos​

Los campos de una secuencia compuesta se materializan en orden de declaración, y cada <gen> consume el PRNG de forma independiente. Eso tiene una consecuencia que vale la pena recordar:

Intercambiar dos etiquetas <gen name="…"> cambia la salida con la misma semilla. El primer campo drena una porción distinta del flujo aleatorio que antes, así que todos los campos que vienen después se desplazan.

<!-- Estos dos producen valores DIFERENTES con el mismo seed -->
<sequence name="Person">
<gen name="FirstName" type="template" value="person.male.firstName"/>
<gen name="Age" type="number" value="18..60"/>
</sequence>

<sequence name="Person">
<gen name="Age" type="number" value="18..60"/>
<gen name="FirstName" type="template" value="person.male.firstName"/>
</sequence>

Si necesita una salida reproducible byte por byte entre ejecuciones, fije el orden de los campos una vez y no reordene los generadores después de haberse comprometido con una semilla. Hay más sobre lo que la semilla sí garantiza y lo que no en Determinismo y proporciones.

Fuentes de valores relacionadas​

Otras dos fuentes nombradas viven junto a <sequence> en <env> y también se leen con ${{Name}}:

  • <mix> — una distribución por porcentaje exacto, donde cada rama puede ser un compuesto completo de literales y generadores (no solo un valor suelto).
  • <switch> — una tabla de búsqueda determinista de clave → valor.

Cuando solo necesita una columna de valores aleatorios o ponderados, la herramienta es <sequence>.

Siguiente​