Saltar al contenido principal

running — un total que se arrastra por la columna

Sirve cuando un valor no se sortea sino que se acumula: el saldo de una cuenta después de cada movimiento, un medidor que solo sube, la carga más alta vista hasta ahora. El valor de la fila 40 depende de las filas 1 a 39.

Todos los demás generadores contestan una fila a partir de su propio índice. Este no puede, y no es una limitación que haya que esquivar: es lo que «acumulado» significa.

<tdc>
<env count="8" seed="ledger" local="en">
<sequence name="Op"><gen type="number" value="-400..500"/></sequence>
<sequence name="Balance"><gen type="running" of="Op" accumulate="sum" base="1000"/></sequence>
</env>
<block>
<line><data>${{Op}} ${{Balance}}</data></line>
</block>
</tdc>
./run ledger.tdc
399   1399
-246   1153
-270   883
159   1042
24   1066
-400   666
419   1085
80   1165
La salida es ilustrativa

Los valores salen de un seed fijo, así que son reproducibles, pero las cadenas exactas pueden cambiar entre versiones del núcleo. Tómelas como ejemplo de forma, no como garantía.

De un vistazo

AtributoObligatorioQué hace
ofLa columna que se acumula. Tiene que estar declarada más arriba que esta secuencia
accumulatesum, min o max
basenoEl valor de apertura — un saldo inicial, un odómetro de partida
resetnoUna columna cuyo cambio reinicia el total

Un total acumulado no sortea nada. Lee una columna que ya existe, no consume azar, y por eso agregar uno deja todas las demás columnas exactamente donde estaban.

reset= — un total por grupo

Sin reset= hay un total para todo el archivo. Con él, la columna se parte en tramos y cada uno se acumula por su cuenta — un saldo por cuenta y no un saldo por corrida.

<env count="9" seed="acct" local="en">
<sequence name="Account"><gen type="text" value="A,A,A,B,B,C,C,C,C" order="sequential"/></sequence>
<sequence name="Op"><gen type="number" value="10..99" decimals="2"/></sequence>
<sequence name="Balance"><gen type="running" of="Op" accumulate="sum" reset="Account"/></sequence>
</env>
./run accounts.tdc
A  96.47  96.47
A  29.18  125.65
A  91.43  217.08
B  15.65  15.65
B  67.87  83.52
C  48.03  48.03
C  39.00  87.03
C  56.04  143.07
C  78.62  221.69

Un tramo termina donde el valor de reset= cambia respecto de la fila anterior, así que los grupos tienen que venir juntos. order="sequential" arriba es una manera de lograrlo; ordenar es otra, aunque de ordenar suele encargarse la base de datos, así que lo habitual es que la columna ya venga agrupada.

Centavos exactos

La aritmética corre sobre enteros escalados por la fracción más ancha de la columna, nunca sobre punto flotante. 96.47 + 29.18 es 125.65 — y es el mismo 125.65 en las cinco implementaciones, cosa que un flotante no garantizaría.

base= entra en esa misma escala. Una apertura de 1000.00 ensancha toda la columna a dos decimales, que es lo que espera quien lee un extracto.

Orden de declaración

of= y reset= nombran una columna, y las dos tienen que estar declaradas más arriba que el total acumulado (TDC240). La razón es la misma que tiene parent: el total se arma con una columna que ya existe.

./run ledger.tdc
error[TDC240]: of="Op" is not a sequence declared above this one
--> ledger.tdc:3:54
|
3 |     <sequence name="Balance"><gen type="running" of="Op" accumulate="sum"/></sequence>
|                                                      ^^
|
note: A running total is built from a column that already exists, so the column it reads has to come first.

Un total acumulado que no dice qué ni cómo acumular es TDC239.

Qué motor lo ejecuta

Los motores de streaming rechazan un total acumulado, y lo nombran al hacerlo; el enrutador manda la configuración al motor en memoria:

./run ledger.tdc --engine 2
tdcv2: a running total ("Balance") is the accumulation of every row before it, so it cannot be computed one row at a time; the in-memory engine handles it (run without a forced streaming engine)

Normalmente nunca ve ese mensaje: el enrutador elige el motor solo, y el rechazo aparece únicamente cuando la configuración nombra un motor de streaming y por lo tanto pidió que se lo dijeran.

Lo que cuesta: un total acumulado vive en memoria durante toda la corrida, como cualquier columna del motor en memoria. Ese es el límite honesto de este generador. Un libro mayor de unos millones de filas está bien; uno de mil millones no es algo que TDC haga, porque todo el sentido de los motores de streaming es que una fila se calcula desde su índice, y acá no se puede.

Todo lo demás sigue en streaming. El límite es por configuración, no por proyecto: una corrida sin total acumulado queda intacta, y el total acumulado dentro de un registroaccumulate= sobre una lista repeat— no cuesta nada y funciona en todos los motores.

Véase también

  • accumulate= sobre una lista repeat — la misma idea dentro de un registro, gratis en todos los motores
  • Contadoresincrement y decrement, que se mueven a paso fijo y sí se calculan desde el índice de la fila
  • Series de tiempo — una curva que sube con tendencia y ruido, también desde el índice solo, y normalmente lo que «un valor que crece» realmente necesita