Отбор через filter
Без filter строка выбирает из всего пула. С ним — только из тех членов, которых
пропускает выражение.
Очевидный случай: пациент северной клиники должен попасть к врачу, который там работает.
<tdc>
<env count="10" seed="clinic" local="en">
<pool name="Doctors" count="6">
<sequence name="clinic"><gen type="text" value="North,South"/></sequence>
<sequence name="name"><gen type="template" value="person.lastName"/></sequence>
</pool>
<sequence name="Clinic"><gen type="text" value="North,South" percent="50,50"/></sequence>
<sequence name="Patient"><gen type="template" value="person.female.firstName"/></sequence>
<sequence name="Seen"><gen type="pool" value="Doctors" filter="clinic == Clinic"/></sequence>
</env>
<block>
<line><data>${{Clinic}} | ${{Patient}} -> Dr. ${{Seen.name}} (${{Seen.clinic}})</data></line>
</block>
</tdc>
South | Barbara -> Dr. Smith (South) North | Mary -> Dr. Jones (North) South | Dorothy -> Dr. Garcia (South) South | Jennifer -> Dr. Johnson (South) North | Elizabeth -> Dr. Jones (North) North | Patricia -> Dr. Jones (North) North | Susan -> Dr. Williams (North) South | Sarah -> Dr. Smith (South) South | Margaret -> Dr. Garcia (South) North | Linda -> Dr. Jones (North)
Клиника пациента и клиника врача совпадают в каждой строке.
Выбор остаётся равномерным
filter решает, какие члены предложены, а не какой из них взят. Среди прошедших
выбор равномерный — северный пациент может попасть к любому из северных врачей.
Это стоит сказать вслух, потому что очевидная альтернатива — «взять первого подходящего» — отдала бы всем северным пациентам одного и того же врача и тихо разрушила разброс, ради которого пул и строился.
Что означает имя внутри filter
Выражение вычисляется сразу в двух областях видимости: поля кандидата и столбцы текущей строки.
| Имя | Что читается |
|---|---|
clinic | поле кандидата, если у пула есть поле с таким именем |
Clinic | столбец текущей строки |
Doctors.clinic | всегда поле кандидата — уточнённая запись |
North | голое слово, читается как строковый литерал |
Порядок важен: голое имя сначала ищется среди полей члена и только потом среди столбцов строки. Имя, которое есть и там и там, не угадывается, а отклоняется:
error[TDC232]: "clinic" in filter= is both a field of pool "Doctors" and a sequence — which one is meant is not decidable
--> clinic.tdc:8:27
|
8 | <sequence name="Seen"><gen type="pool" value="Doctors" filter="clinic == clinic"/></sequence>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
note: Rename one of them. Qualifying one side ("Doctors.clinic") does not help: the other "clinic" still reads as the member's field, so the test would compare a value with itself.Уточнённое имя, которого у пула нет, тоже ловится:
error[TDC226]: filter= reads "Doctors.branch", but pool "Doctors" has no field "branch" --> clinic.tdc:7:27 | 7 | <sequence name="Seen"><gen type="pool" value="Doctors" filter="Doctors.branch == Site"/></sequence> | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | note: Fields of "Doctors": clinic.
Неуточнённое незнакомое имя намеренно не трогают: язык выражений читает голое слово
как строковый литерал, и именно так filter="clinic == North" говорит «только северные
врачи», ничего не объявляя.
Это полноценное выражение
поле == Столбец — частая форма, но в filter можно писать всё, что понимает
язык выражений: !=, <,
>, <=, >=, &&, ||, ! и арифметику.
Отсюда случаи поинтереснее клиники — покупатель берёт то, что ему по карману:
<tdc>
<env count="8" seed="shop" local="en">
<pool name="Catalog" count="6">
<sequence name="item" uniq="true"><gen type="text" value="Kettle,Lamp,Chair,Desk,Rug,Clock"/></sequence>
<sequence name="price"><gen type="number" value="10..300"/></sequence>
</pool>
<sequence name="Budget"><gen type="number" value="50..250"/></sequence>
<sequence name="Buys"><gen type="pool" value="Catalog" filter="price <= Budget"/></sequence>
</env>
<block>
<line><data>budget ${{Budget}} -> ${{Buys.item}} at ${{Buys.price}}</data></line>
</block>
</tdc>
budget 232 -> Clock at 11 budget 124 -> Desk at 92 budget 61 -> Clock at 11 budget 148 -> Clock at 11 budget 208 -> Rug at 198 budget 54 -> Clock at 11 budget 102 -> Desk at 92 budget 60 -> Clock at 11
Никто не покупает дороже своего бюджета, и перечислять руками ничего не пришлось.
<= и && как естьTDC не разворачивает XML-сущности. filter="price <= Budget" дойдёт до разборщика
именно этими девятью символами и не сработает. Пишите тот оператор, который имеете в виду.
Чего это стоит
Есть два пути, и какой из них сработает — решает то, как написан фильтр:
| Фильтр | Как отвечается строка |
|---|---|
поле == Столбец (в любом порядке) | пул один раз раскладывается по корзинам; строка стоит один поиск |
| всё остальное | кандидаты перебираются на каждой строке — линейно по размеру пула |
Оба варианта правильные. Разница в том и объясняет, зачем у пула вообще есть потолок размера: перебор миллиона членов две тысячи раз — это реальная цена, и потолок ровно там, где инструмент об этом говорит.
filter — это не if
Оба сужают, оба могут стоять на одном <gen>. Различаются они результатом:
| О чём спрашивает | Что происходит при «нет» | |
|---|---|---|
if | о строке — один ответ на строку | ничего не генерируется, ячейка пустая |
filter | о каждом кандидате — один ответ на члена | подставляется подходящая запись, пустой ячейки не бывает |
То есть if="Age >= 18" оставит несовершеннолетних без врача, а
filter="clinic == Clinic" даст врача всем, но из нужной клиники. Вместе они читаются
как «только взрослые и только из своей клиники»:
<gen type="pool" value="Doctors" if="Age >= 18" filter="clinic == Clinic"/>
Они и спрашивают о разном — поэтому одним атрибутом обе работы делать нельзя. if
спрашивает один раз на строку. filter — один раз на каждого кандидата, то есть тридцать
вопросов на строку для пула из тридцати.
Когда никто не подошёл
Раз filter не оставляет пустых ячеек, «никто не подошёл» — это ошибка, а не пропуск.
Сообщение называет строку и то значение, которое сузило выбор до нуля:
tdcv2: pool "Doctors": no member satisfies filter="clinic == Clinic" for row 3 (Clinic="South"). A filter narrows the members a row may draw from; when it narrows them to none there is nothing to substitute. Add a member that matches, or widen the filter.
Это отказ во время прогона, а не ошибка проверки, и иначе быть не может: валидатор не
знает, что ни один член не выйдет South, пока пул не разыгран. Два способа починить
названы в сообщении — добавить подходящего члена или расширить фильтр, — а есть и третий:
задать полю пула тот же конечный список, из которого разыгрывается столбец строки, чтобы
каждое значение было представлено.
Рядом
- Обзор — что такое пул и тот потолок размера, о котором здесь речь
- Связывание пулов —
filter, читающий поле другой ссылки на пул: так строится цепочка - Условия —
ifцеликом, включая операторы, общие у него сfilter