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

Сравнение и истинность

Всё, что производит конфигурация 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>
./run code.tdc
Code = "1": == 1 === 1
Code = "01": == 1
Code = "1.0": == 1
Code = "+1": == 1

Все четыре значения являются числом один, поэтому == 1 истинно везде. Только первое печатается как 1, поэтому === 1 истинно один раз.

== — то же число

Правило, по шагам:

  1. Если обе стороны — целые числа, сравниваем их как целые. Точно: у 19-значного идентификатора сохранится каждая цифра, потому что целые числа здесь никогда не становятся плавающими. См. Целые числа.
  2. Иначе, если одна сторона — число, написанное вами, а другая — текст, читающийся как число, сравниваем их как числа.
  3. Иначе сравниваем как текст.

Шаг 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>
./run money.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>
./run status.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>
./run pair.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>
./run loose.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>
./run ages.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>
./run truth.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>
./run spike.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>
./run join.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 — отвечают на каждое сравнение с этой страницы одинаково, и общие фикстуры держат их на этом. См. Детерминизм.