Пулы — строка ссылается на целую запись
Генератор выдаёт значение. Последовательность — это столбец таких значений. Этого хватает почти на всё, что нужно конфигу, — а потом упирается в стену.
Две тысячи пациентов принимают тридцать врачей. В строке пациента нужны имя, фамилия и
кабинет врача. Три последовательности рядом дают три независимых розыгрыша, и в строке
оказывается имя одного врача рядом с кабинетом другого. Данные выглядят правдоподобно и
при этом бессмысленны: Dr. John Williams, room 118, где такого врача не существует.
Не хватает мысли, что врач — это не значение, а запись, и тридцать таких записей
существуют до того, как появится первый пациент. <pool> — это и есть такая таблица.
<tdc>
<env count="8" seed="clinic" local="en">
<pool name="Doctors" count="4">
<sequence name="firstName"><gen type="template" value="person.male.firstName"/></sequence>
<sequence name="lastName"><gen type="template" value="person.lastName"/></sequence>
<sequence name="room"><gen type="number" value="100..199"/></sequence>
</pool>
<sequence name="Patient"><gen type="template" value="person.female.firstName"/></sequence>
<sequence name="Seen"><gen type="pool" value="Doctors"/></sequence>
</env>
<block>
<line><data>${{Patient}} -> Dr. ${{Seen.firstName}} ${{Seen.lastName}}, room ${{Seen.room}}</data></line>
</block>
</tdc>
Barbara -> Dr. James Johnson, room 100 Mary -> Dr. Michael Smith, room 186 Margaret -> Dr. Robert Brown, room 148 Jennifer -> Dr. John Williams, room 154 Elizabeth -> Dr. Michael Smith, room 186 Patricia -> Dr. Michael Smith, room 186 Susan -> Dr. John Williams, room 154 Linda -> Dr. James Johnson, room 100
Значения получены с фиксированным seed, то есть воспроизводимы, но конкретные строки
могут отличаться между версиями ядра. Смотрите на них как на пример формы, а не как на
гарантию.
Четыре врача обслуживают восемь пациентов, и три поля каждого врача держатся вместе. Джеймс Смит сидит в кабинете 197 в каждой строке, где он появляется, потому что строка выбрала Смита, а не «имя, фамилию и кабинет».
- Aпул: четыре члена, построенные один раз до прогона
- Bсгенерированные строки
- Cодин выбор на строку — обе ячейки строки всегда от одного и того же члена
Происходит две вещи, именно в таком порядке
- Строится пул. До того как появится хоть одна строка, TDC создаёт
countчленов. Это происходит один раз — и для прогона на 8 строк, и на 8 миллиардов. - Каждая строка берёт одного члена.
<gen type="pool">даёт своей последовательности одного целого члена на строку и публикует все его поля под именемСсылка.поле.
Всё остальное на этой странице следует из этих двух предложений.
Кратко
<pool> живёт прямо в <env>, рядом с
<sequence>. Напрямую его не читают: из него разыгрывает последовательность.
У <pool> | Обязателен | Что задаёт |
|---|---|---|
name | да | Имя, которое <gen type="pool"> называет в value |
count | да | Сколько членов в таблице |
comment | нет | Свободный комментарий |
У <gen type="pool"> | Обязателен | Что задаёт |
|---|---|---|
value | да | Из какого пула разыгрывать |
filter | нет | Из каких членов эта строка может выбирать — Отбор |
if | нет | Достанется ли строке член вообще — Отбор |
Пул — это миниатюрный <env>
Это то решение в устройстве, которое стоит знать: оно отвечает на большинство вопросов
до того, как вы их зададите. Тело пула строится тем же механизмом, что и <env> конфига,
только вместо числа строк работает число членов.
Поэтому всё, что вы уже знаете, действует и внутри пула:
| Внутри пула | Означает |
|---|---|
<sequence> | поле каждого члена |
<mix> | поле, разделённое точным percent — по ЧЛЕНАМ |
<switch> | поле, выведенное из другого поля того же члена |
<uniq> · uniq="true" | члены, которые должны отличаться друг от друга |
<distinct> | два поля одного члена, которые должны отличаться |
if= | поле, которое есть только у части членов |
parent= | поле, построенное по тем членам, которых выбрало другое поле |
<compute> | поле, вычисленное из других полей члена |
Чего в пуле быть не может — это вывода: ни <block>, ни теги обрамления. Пул это
таблица, которую читают другие столбцы, а не то, что пишется в файл. Ещё в нём не может
быть другого <pool> — пулы остаются плоскими таблицами, и один
ссылается на другой, а не вкладывается в него.
Два имени и точка
Имя есть у пула и у последовательности, которая из него разыгрывает. Это разные вещи:
<pool name="Doctors" count="4"> … </pool>
<sequence name="Seen"><gen type="pool" value="Doctors"/></sequence>
Doctors— таблица. В<block>на неё никто не ссылается.Seen— столбец, в котором на каждой строке лежит один врач. Его поля читаются как${{Seen.firstName}}.
Из одного пула могут разыгрывать две последовательности, и выбирают они независимо:
Seen и Referred в одной строке дадут двух разных врачей — обычно это и нужно.
Прочитать ссылку без поля нельзя — печатать нечего:
error[TDC229]: "Seen" draws a whole member from a pool — it has no value of its own to print
--> clinic.tdc:8:16
|
8 | <block><line><data>${{Seen}}</data></line></block>
| ^^^^^^^^^^^^^^^^^^^^^^
|
note: Read a field: ${{Seen.name}}.Один выбор на строку
В последовательности одно значение на строку. Ссылка на пул — это последовательность,
значит в ней один член на строку, и все ${{Seen.…}} в этой строке читают одного и
того же.
Это правило и есть смысл конструкции. Из него же следует, почему «Дмитрий Иванова» пул выдать не может: пол, имя и фамилия — поля одного члена, и строка, взявшая члена, взяла все три.
Выбор равномерный по членам — это розыгрыш, а не график дежурств, потому что долей никто не объявлял; см. объявленные доли или розыгрыш. Он ещё и адресуемый: строка 900 000 000 находит своего врача без того, чтобы 899 999 999 предыдущих вообще существовали. Поэтому пул одинаково дёшев на всех движках.
Доли внутри пула
Раз пул строится обычным механизмом, percent работает внутри него так же, как на
верхнем уровне. Тридцать процентов врачей — хирурги:
<pool name="Doctors" count="10">
<mix name="role" percent="30,70">
<case><gen type="text" value="surgeon"/></case>
<case><gen type="text" value="therapist"/></case>
</mix>
<sequence name="name"><gen type="template" value="person.lastName"/></sequence>
</pool>
Доля считается от десяти членов, а не от строк. Хирургов трое; как часто к этим троим
попадают — зависит от того, как разыгрываются строки, и от filter,
который превращает «трое из врачей — хирурги» в «пациент, которому нужен хирург, к
хирургу и попадает».
Члены, отличающиеся друг от друга
uniq внутри пула означает то же, что и снаружи: ни у каких двух членов нет одинакового
значения.
<pool name="Rooms" count="6">
<sequence name="number" uniq="true"><gen type="number" value="100..199"/></sequence>
</pool>
Шесть разных номеров кабинетов, розыгрыш без возврата. Пулу, члены которого задуманы как разные вещи, обычно нужно это хотя бы на одном поле — иначе два члена могут выйти одинаковыми, и строка их не различит.
Собственный сид пула
Пул не разыгрывается из основного потока прогона. У него свой производный сид:
<сид прогона> + "#pool:" + <имя пула>
Пул Doctors при seed="main" строится из main#pool:Doctors. Атрибута seed у
<pool> нет — весь механизм в этом выводе, а попытка его написать сообщается (TDC015).
Почему выводится, а не берётся. Если бы пул тянул из основного потока, его добавление сдвинуло бы все колонки, объявленные ниже: id остались бы, а возрасты, имена и даты уехали, и вчерашний снимок перестал бы совпадать по причине, которую в диффе не видно. Производный сид делает пул невидимым для всего, что он не кормит.
Два следствия, которые стоит знать:
- Меняете
seedпрогона — меняется и пул. Это и задумано: одно число по-прежнему воспроизводит весь прогон целиком. - Переименование пула меняет его состав, потому что имя входит в производный сид. Переименование здесь не косметика; если важен конкретный набор членов, имя лучше не трогать.
Размер
Пул держится в памяти весь прогон — именно поэтому обращение к нему из строки бесплатно. На эталонной реализации замерено: член с четырьмя полями стоит около 320 байт.
| Членов | Примерно | Что происходит |
|---|---|---|
| до 100 000 | до ~29 МБ | ничего, работает |
| больше 100 000 | TDC234, предупреждение | |
| больше 1 000 000 | больше ~290 МБ | TDC235, отказ |
Оба сообщения заканчиваются одинаково, потому что причина обычно одна: если имелось в
виду число строк, то это count у <env>.
Потолок касается таблицы, а не прогона. Пул из тридцати врачей обслуживает миллиард строк пациентов ровно так же дёшево, как тысячу, и работает на всех движках.
Куда дальше
- Отбор через
filter— из каких членов может выбирать строка и чем это отличается отif - Связывание пулов — несколько пулов, пул, разыгрывающий из другого пула, и цепочки в три уровня