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
nameEl 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: Justina Cifuentes
H: Bonifacio Ordóñez (Soldado)
M: Silvia Guerra
M: Azucena Mesa
H: Faustino Posada (Capitán)
H: Justo 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