Сравнение и истинность
Всё, что производит конфигурация TDC, — это текст. <gen type="number"> производит
текст 42, пакет данных производит текст Иванов, и каждая
встроенная переменная — тоже текст. Тип рядом со значением не хранится,
потому что он никому не нужен: в CSV-файл в конце попадает именно текст.
Не текст только то, что вы пишете сами внутри выражения: 1 в if="N == 1",
admin в if="Role == admin".
Из этого одного факта следует каждое правило на этой странице.
Два вопроса, два оператора
Спрашивая, равны ли две вещи, вы задаёте один из двух разных вопросов:
- Это одно и то же число?
01и1— да. - Это один и тот же текст?
01и1— нет.
Оба вопроса нужны, поэтому у каждого свой оператор.
| Оператор | Спрашивает | "01" против 1 |
|---|---|---|
== != | то же число? | истина |
=== !== | тот же текст? | ложь |
К такой же форме пришёл Perl, и по той же причине: там значение — это тоже текст, который может читаться как число, поэтому один оператор спрашивает про число, а другой — про символы.
<tdc>
<env count="4" seed="cmp" local="ru">
<sequence name="Code"><gen type="text" value="1,01,1.0,+1" order="sequential"/></sequence>
</env>
<block>
<line><data>Code = "${{Code}}":</data><data if="Code == 1"> == 1</data><data if="Code === 1"> === 1</data></line>
</block>
</tdc>
Code = "1": == 1 === 1 Code = "01": == 1 Code = "1.0": == 1 Code = "+1": == 1
Все четыре значения являются числом один, поэтому == 1 истинно везде. Только первое
печатается как 1, поэтому === 1 истинно один раз.
== — то же число
Правило, по шагам:
- Если обе стороны — целые числа, сравниваем их как целые. Точно: у 19-значного идентификатора сохранится каждая цифра, потому что целые числа здесь никогда не становятся плавающими. См. Целые числа.
- Иначе, если одна сторона — число, написанное вами, а другая — текст, читающийся как число, сравниваем их как числа.
- Иначе сравниваем как текст.
Шаг 3 — то, из-за чего работает Role == admin: числа нет ни с одной стороны, обе остаются
текстом.
Шаг 2 — то, из-за чего работает денежная колонка:
<tdc>
<env count="3" seed="money" local="ru">
<sequence name="Total"><gen type="text" value="100.00,0.00,99.50" order="sequential"/></sequence>
</env>
<block>
<line><data>${{Total}}:</data><data if="Total == 100"> сотня</data><data if="Total == 0"> ноль</data><data if="Total < 100"> меньше</data></line>
</block>
</tdc>
100.00: сотня 0.00: ноль меньше 99.50: меньше
Шагу 2 нужно число, написанное вами. Две колонки, сравниваемые друг с другом, в
арифметику из-за него не втягиваются — но шаг 1 работает и там, поэтому две колонки из цифр
сравниваются как целые числа. "01" == "1" истинно; "" == " " ложно, хотя оба читаются
как ноль.
=== — тот же текст
Одно правило без шагов: напечатать обе стороны и сравнить символы.
- Колонка печатается тем, что в ней лежит.
- Написанное вами число печатается так же, как попало бы в вывод:
1— это1,1.5— это1.5. trueиfalseпечатаются этими четырьмя и пятью буквами.- Ничто — пустая колонка — печатается пустым текстом.
<tdc>
<env count="5" seed="http" local="ru">
<sequence name="Status"><gen type="text" value="200,404,500,200,301" order="sequential"/></sequence>
</env>
<block>
<line><data>${{Status}}</data><data if="Status === 200"> успех</data><data if="Status >= 400"> ошибка</data></line>
</block>
</tdc>
200 успех 404 ошибка 500 ошибка 200 успех 301
Где они расходятся
Только там, где число и символы действительно различаются. Вот формы, которые встречаются на практике:
| В колонке лежит | == 1 | === 1 | Почему |
|---|---|---|---|
1 | истина | истина | то же число, те же символы |
01 | истина | ложь | ведущий ноль — это символ |
1.0 | истина | ложь | выписанная дробная часть — тоже символы |
+1 | истина | ложь | и знак плюса тоже |
1 (с пробелом) | истина | ложь | и пробел тоже |
один | ложь | ложь | ни число, ни эти символы |
Между двумя колонками деление такое же, и никакого литерала рядом нет:
<tdc>
<env count="4" seed="pair" local="ru">
<sequence name="A"><gen type="text" value="01,7,x,0" order="sequential"/></sequence>
<sequence name="B"><gen type="text" value="1,7,x,00" order="sequential"/></sequence>
</env>
<block>
<line><data>${{A}} и ${{B}}:</data><data if="A == B"> то же число</data><data if="A === B"> тот же текст</data></line>
</block>
</tdc>
01 и 1: то же число 7 и 7: то же число тот же текст x и x: то же число тот же текст 0 и 00: то же число
Чтение текста как числа щедрое
Шаг 2 оператора == читает число так же, как читает его весь остальной язык, и принимает
больше, чем можно ожидать: пробелы по краям игнорируются, пустое значение читается как ноль,
а 0x10 — как шестнадцать.
<tdc>
<env count="4" seed="loose" local="ru">
<sequence name="Field"><gen type="text" value="7,0x10,007,x" order="sequential"/></sequence>
</env>
<block>
<line><data>"${{Field}}":</data><data if="Field == 7"> == 7</data><data if="Field == 16"> == 16</data><data if="Field === '007'"> === '007'</data></line>
</block>
</tdc>
"7": == 7 "0x10": == 16 "007": == 7 === '007' "x":
Под === ничего этого не происходит: он не читает ничего ни как что. Когда вопрос именно о
символах — идентификатор фиксированной ширины, код с ведущим нулём, шестнадцатеричное поле —
отвечает на него ===.
Порядок
<, >, <= и >= всегда читают обе стороны как числа. Текстового порядка нет: два
значения, не являющиеся числами, не больше и не меньше друг друга, поэтому оба сравнения
ложны.
<tdc>
<env count="6" seed="ord" local="ru">
<sequence name="Age"><gen type="text" value="15,17,18,25,40,70" order="sequential"/></sequence>
</env>
<block>
<line><data>возраст ${{Age}}:</data><data if="Age < 18"> младше18</data><data if="Age >= 18"> взрослый</data><data if="Age > 65"> пенсионер</data></line>
</block>
</tdc>
возраст 15: младше18 возраст 17: младше18 возраст 18: взрослый возраст 25: взрослый возраст 40: взрослый возраст 70: взрослый пенсионер
Два целых числа упорядочиваются точно — и за той границей, где плавающее уже округляло бы.
Что считается истиной
Голое имя само по себе является условием: if="Flag". Как и всё, что получают !, && и
||. Ложны два текста, а любой другой текст истинен:
| Значение | Истинность | Причина |
|---|---|---|
| `` (пусто) | ложь | колонка не произвела значения |
false | ложь | колонка-флаг, говорящая «нет» |
0 | истина | ноль — это значение, а не отсутствие |
00, 0.0 | истина | то же самое |
(пробел) | истина | пробел — это символ |
| всё остальное | истина |
<tdc>
<env count="6" seed="truth" local="ru">
<sequence name="V"><gen type="text" value="x,0,00,0.0,false,true" order="sequential"/></sequence>
</env>
<block>
<line><data>"${{V}}"</data><data if="V"> истина</data><data if="!V"> ложь</data></line>
</block>
</tdc>
"x" истина "0" истина "00" истина "0.0" истина "false" ложь "true" истина
Это правило Lua и Ruby — ложны только «ничто» и «нет» — перенесённое в язык, у которого единственный носитель значения — текст. Два ложных текста TDC это ровно те же две вещи:
- пусто — то, как колонка сообщает, что ничего не произвела:
missing=, не сработавшийif=, веткаparent=, не принадлежащая этой строке; false— булево «нет», записанное единственным способом, каким его может записать колонка.
0 истинен, и это то, что стоит запомнитьif="Count" спрашивает, произвела ли колонка значение. Нулевой счётчик его произвёл. Если
вопрос про число, берите оператор, который означает число: if="Count != 0".
Почему работает колонка-флаг
_first, _last и каждая колонка anomaly_flag хранят
буквальный текст true или false. Поскольку false ложен, флаг читается как условие
напрямую:
<tdc>
<env count="6" seed="flag" local="ru">
<sequence name="Amount"><gen type="number" value="10..99" anomaly="0.4" anomaly_factor="20" anomaly_flag="Spike"/></sequence>
</env>
<block>
<line><data>${{Amount}} Spike=${{Spike}}</data><data if="Spike"> ВЫБРОС</data><data if="Spike === 'true'"> ===true</data><data if="Spike == true"> ==true</data></line>
</block>
</tdc>
89 Spike=false 30 Spike=false 76 Spike=false 47 Spike=false 1500 Spike=true ВЫБРОС ===true 1000 Spike=true ВЫБРОС ===true
Обратите внимание на третью метку: Spike == true не появляется нигде. == сравнивает
числа, а ни текст true, ни булево значение числом не являются. Пишите голое имя или
=== 'true'.
То же правило заставляет работать приём с запятой между элементами:
<tdc>
<env count="4" seed="join" local="ru">
<sequence name="Tag"><gen type="text" value="красный,зелёный,синий,серый" order="sequential"/></sequence>
</env>
<block>
<line><data>${{Tag}}</data><data if="!_last">, </data></line>
</block>
</tdc>
красный, зелёный, синий, серый
Два селектора, которые всегда текст
parent="Code.1" и <case is="1"> — это не сравнения, а селекторы значения: они
спрашивают, какое значение колонка произвела на этой строке. Совпадение идёт по символам,
как у ===, и никакого чтения как числа не происходит:
| В колонке лежит | parent="Code.1" | <case is="1"> | == 1 |
|---|---|---|---|
1 | совпадает | совпадает | истина |
01 | нет | нет | истина |
1.0 | нет | нет | истина |
Если колонку из цифр надо сопоставить по числу, делайте это через if= и ==. А case
принимает несколько ключей сразу — <case is="1|01|1.0">, — и это более короткий ответ,
когда колонка держит фиксированный набор написаний.
in
in берёт справа список и спрашивает, входит ли левое значение в него. Сравнивает он так же
мягко, как ==, поэтому колонка из цифр всё ещё совпадает со списком чисел.
<gen if="Country in [US, CA, MX]" type="text" value="NAFTA"/>
Голые слова внутри списка остаются голыми словами, ровно как и рядом с ==. Больше нигде
список не разрешён — см. TDC259.
За чем браться
| Вопрос | Оператор |
|---|---|
| Равна ли эта сумма / возраст / счётчик числу N? | == |
| Это та самая категория или статус? | == или === |
| Ровно ли эти символы в идентификаторе? | === |
| Сохранился ли у кода ведущий ноль? | === |
| Взведён ли флаг? | голое имя |
| Произвела ли колонка хоть что-нибудь? | голое имя |
| Это число больше или меньше? | < > <= >= |
| Это одно из нескольких? | in |
Когда подошли бы оба, == читается естественнее и является повседневным выбором. Беритесь
за ===, как только начинают иметь значение точные символы.
Приоритет
От высшего к низшему:
! → * / % → + - → < > <= >= → == != === !== → && → ||
Скобки его переопределяют. a ? b : c связывает слабее всего.
Одни и те же пять ответов
Все пять реализаций — TypeScript, Python, Rust, C# и Java — отвечают на каждое сравнение с этой страницы одинаково, и общие фикстуры держат их на этом. См. Детерминизм.