Перейти к основному содержимому

running — итог, который переносится по колонке

Когда нужен: значение не разыгрывается, а накапливается: остаток на счёте после каждой операции, счётчик, который только растёт, самая большая нагрузка за всё время. Значение сороковой строки зависит от строк с первой по тридцать девятую.

Все остальные генераторы отвечают на строку по её номеру. Этот не может — и это не ограничение, которое надо обходить, а ровно то, что слово «нарастающий» и значит.

<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
Вывод показан для примера

Значения получены с фиксированным seed, то есть воспроизводимы, но точные строки могут отличаться между версиями ядра. Смотрите на форму, а не на конкретные символы.

Коротко

АтрибутОбязателенЧто делает
ofдаКолонка, которую накапливаем. Должна быть объявлена выше этой последовательности
accumulateдаsum, min или max
baseнетНачальное значение — входящий остаток, начальный пробег
resetнетКолонка, смена значения которой перезапускает итог

Нарастающий итог ничего не разыгрывает. Он читает уже существующую колонку, не расходует ни одного случайного числа — и потому добавление такой колонки не сдвигает ни одну другую.

reset= — свой итог на каждую группу

Без reset= итог один на весь файл. С ним колонка делится на отрезки, и каждый накапливается сам по себе — остаток по счёту, а не остаток по запуску.

<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

Отрезок кончается там, где значение reset= изменилось по сравнению с прошлой строкой, — значит, группы должны идти подряд. order="sequential" выше это один способ; сортировка другой, но сортировкой обычно занимается база, так что чаще всего колонка и так приходит сгруппированной.

Точные копейки

Арифметика идёт на целых числах, умноженных на самую широкую дробь в колонке, и ни разу не на плавающей точке. 96.47 + 29.18 это 125.65 — и это одно и то же 125.65 во всех пяти реализациях, чего плавающая точка не гарантирует.

base= участвует в том же масштабе. Входящий остаток 1000.00 расширяет всю колонку до двух знаков — именно так, как ожидает тот, кто читает выписку.

Порядок объявления

of= и reset= называют колонку, и обе должны быть объявлены выше нарастающего итога (TDC240). Причина та же, что у parent: итог строится из колонки, которая уже существует.

./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.

Нарастающий итог, который не сказал, что и как накапливать, — это TDC239.

На каком движке это считается

Потоковые движки отказываются считать нарастающий итог — прямо называя его — и маршрутизатор отправляет конфигурацию на движок в памяти:

./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)

Обычно этого сообщения вы не увидите: движок выбирает маршрутизатор, а отказ всплывает только тогда, когда конфигурация назвала потоковый движок сама — то есть попросила, чтобы ей сказали.

Чего это стоит: нарастающий итог живёт в памяти весь запуск, как любая колонка движка в памяти. Это честная граница этого генератора. Выписка на несколько миллионов строк — нормально; выписка на миллиард строк — не то, что делает TDC, потому что весь смысл потоковых движков в том, что строка считается по своему номеру, а здесь — не считается.

Всё остальное по-прежнему течёт потоком. Ограничение действует на конфигурацию, а не на проект: запуск без нарастающего итога не затронут, а нарастающий итог внутри одной записиaccumulate= на списке repeat — не стоит вообще ничего и работает на любом движке.

См. также

  • accumulate= на списке repeat — та же идея внутри одной записи, бесплатно на любом движке
  • Счётчикиincrement и decrement: они двигаются фиксированным шагом и считаются по номеру строки
  • Временные ряды — кривая с трендом и шумом, тоже по одному номеру строки, и обычно это и есть то, что нужно, когда говорят «значение, которое растёт»