Если я хочу сравнить два числа (или другие хорошо упорядоченные объекты), я бы сделал это с x < y
. Если я захочу сравнить три из них, ученик средней школы по алгебре предложит попробовать x < y < z
. Затем программист во мне ответит: «Нет, это не правильно, вы должны это сделать x < y && y < z
».
Кажется, что большинство языков, с которыми я сталкивался, не поддерживают этот синтаксис, что странно, учитывая, насколько часто он встречается в математике. Python является заметным исключением. JavaScript выглядит как исключение, но на самом деле это просто неудачный побочный продукт приоритета операторов и неявных преобразований; в node.js 1 < 3 < 2
оценивает true
, потому что это действительно так (1 < 3) < 2 === true < 2 === 1 < 2
.
Итак, мой вопрос заключается в следующем: почему x < y < z
не доступно на языках программирования с ожидаемой семантикой?
static bool IsInRange<T>(this T candidate, T lower, T upper) where T : IComparable<T>
если это действительно мешает вам видеть&&
s)Ответы:
Это бинарные операторы, которые в цепочке обычно и естественно создают абстрактное синтаксическое дерево, подобное:
Когда вычисляется (что вы делаете из листьев), это приводит к логическому результату
x < y
, затем вы пытаетесь сделать ошибку типаboolean < z
. Для того,x < y < z
чтобы работать так, как вы обсуждали, вы должны создать специальный случай в компиляторе для создания синтаксического дерева, такого как:Не то чтобы это было невозможно. Это, конечно, так, но это добавляет некоторую сложность синтаксическому анализатору для случая, который действительно не возникает так часто. Вы в основном создаете символ, который иногда действует как бинарный оператор, а иногда эффективно действует как троичный оператор, со всеми последствиями обработки ошибок и тому подобного, что влечет за собой. Это добавляет много места для того, чтобы что-то пошло не так, что разработчики языка скорее избегали бы, если это возможно.
источник
x<y<z
средства, или , что более важно,x<y<=z
. Этот ответ интерпретируетсяx<y<z
как троичный оператор. Именно так не следует интерпретировать это четко определенное математическое выражение.x<y<z
вместо стенографии(x<y)&&(y<z)
. Индивидуальные сравнения все еще бинарные.Почему
x < y < z
обычно не доступно на языках программирования?В этом ответе я заключаю, что
Введение
Я могу говорить с точки зрения Pythonist по этому вопросу. Я являюсь пользователем языка с этой функцией, и мне нравится изучать детали реализации языка. Помимо этого, я немного знаком с процессом изменения языков, таких как C и C ++ (стандарт ISO регулируется комитетом, а версия определяется по годам), и я наблюдал, как в Ruby и Python вносятся принципиальные изменения.
Документация и реализация Python
Из документации / грамматики мы видим, что мы можем связать любое количество выражений с операторами сравнения:
и документация далее заявляет:
Логическая эквивалентность
Так
логически эквивалентны с точки зрения оценки
x
,y
иz
, за исключением того ,y
вычисляется дважды:Опять же, разница в том, что у оценивается только один раз
(x < y <= z)
.(Обратите внимание, скобки совершенно не нужны и излишни, но я использовал их в интересах тех, кто прибывает из других языков, и приведенный выше код является вполне допустимым Python.)
Проверка разобранного абстрактного синтаксического дерева
Мы можем проверить, как Python анализирует цепочки операторов сравнения:
Таким образом, мы видим, что Python или любой другой язык действительно не трудно разобрать.
И вопреки принятому в настоящее время ответу, троичная операция - это общая операция сравнения, которая принимает первое выражение, итерацию конкретных сравнений и итерацию узлов выражения для оценки по мере необходимости. Просто.
Вывод по Python
Лично я нахожу семантику диапазона довольно элегантной, и большинство знакомых мне Python-профессионалов будут поощрять использование этой функции, а не считать ее разрушительной - семантика довольно четко изложена в хорошо известной документации (как отмечено выше).
Обратите внимание, что код читается гораздо больше, чем написано. Изменения, которые улучшают читабельность кода, должны приниматься, а не сбрасываться со счетов, вызывая общие призраки Страха, Неопределенности и Сомнения .
Так почему же x <y <z обычно не доступно в языках программирования?
Я думаю, что есть стечение причин, которые сосредоточены вокруг относительной важности функции и относительного импульса / инерции изменений, разрешенных управляющими языками.
Подобные вопросы можно задать о других более важных языковых особенностях
Почему множественное наследование недоступно в Java или C #? Здесь нет хорошего ответа на любой вопрос . Возможно, разработчики были слишком ленивы, как утверждает Боб Мартин, и приведенные причины являются лишь оправданиями. И множественное наследование - довольно большая тема в информатике. Это, безусловно, важнее, чем цепочка операторов.
Существуют простые обходные пути
Цепочка операторов сравнения элегантна, но отнюдь не так важна, как множественное наследование. И так же, как Java и C # имеют обходные пути интерфейсов, так и каждый язык для множественных сравнений - вы просто объединяете сравнения с логическими "и", что работает достаточно легко.
Большинство языков регулируются комитетом
Большинство языков развиваются комитетом (вместо того, чтобы иметь разумного Доброжелательного Диктатора Для Жизни, как у Питона). И я полагаю, что этот вопрос просто не получил достаточной поддержки, чтобы выйти из его соответствующих комитетов.
Могут ли языки, которые не предлагают эту функцию, измениться?
Если язык позволяет
x < y < z
без ожидаемой математической семантики, это было бы серьезным изменением. Если бы он не позволял это во-первых, добавить это было бы почти тривиально.Ломать изменения
Что касается языков с критическими изменениями: мы обновляем языки с критическими изменениями в поведении, но пользователям, как правило, это не нравится, особенно пользователям функций, которые могут быть повреждены. Если пользователь полагается на прежнее поведение
x < y < z
, они, вероятно, будут громко протестовать. И поскольку большинство языков управляется комитетом, я сомневаюсь, что у нас будет много политической воли, чтобы поддержать такие изменения.источник
x < y < z
к(x < y) && (y < z)
. Выбирая гниды, ментальная модель для цепного сравнения - общая математика. Классическое сравнение - это не математика в целом, а логическая логика.x < y
выдает двоичный ответ{0}
.y < z
выдает двоичный ответ{1}
.{0} && {1}
производит описательный ответ. Логика составлена, а не наивно скована.x<y<z
. У языка когда-то был шанс получить что-то подобное, и этот шанс есть у самого языка.I have watched both Ruby and Python implement breaking changes.
Для тех , кому любопытно, вот изменение ломка в C # 5.0 с участием переменных цикла и закрытия: blogs.msdn.microsoft.com/ericlippert/2009/11/12/...Компьютерные языки пытаются определить наименьшие возможные единицы измерения и позволяют вам их комбинировать. Наименьшей возможной единицей будет что-то вроде «x <y», которая дает логический результат.
Вы можете попросить троичного оператора. Примером может быть x <y <z. Какие комбинации операторов мы допускаем? Очевидно, x> y> z или x> = y> = z или x> y> = z или, возможно, x == y == z должно быть разрешено. Что насчет x <y> z? х! = у! = г? Что означает последнее: x! = Y и y! = Z или что все три разные?
Теперь продвижение аргументов: в C или C ++ аргументы будут преобразованы в общий тип. Итак, что x <y <z означает, что x двойное, а y и z длинные long int? Все три повышены в два раза? Или y берется как double один раз и как long long в другой раз? Что произойдет, если в C ++ один или оба оператора перегружены?
И наконец, вы допускаете любое количество операндов? Как <b> c <d> e <f> g?
Ну, все становится очень сложно. Теперь я не возражаю против того, чтобы x <y <z вызывал синтаксическую ошибку. Потому что полезность этого невелика по сравнению с ущербом, нанесенным новичкам, которые не могут понять, что на самом деле делает x <y <z.
источник
x + y + z
, с той лишь разницей, что это не подразумевает какой-либо семантической разницы. Так что просто ни один известный язык никогда не обращал на это внимания.x < y < z
эквивалентно,((x < y) and (y < z))
но сy
оценкой только один раз ), которую я представляю, что скомпилированные языки оптимизируют свой путь. «Потому что его полезность невелика по сравнению с ущербом, который наносят новичкам, которые не могут понять, что на самом деле делает x <y <z». Я думаю, что это невероятно полезно. Вероятно, будет -1 за это ...a < b > c < d > e < f > g
, с «очевидным» значением(a < b) and (b > c) and (c < d) and (d > e) and (e < f) and (f > g)
. То, что ты можешь писать, не значит, что ты должен писать. Устранение таких чудовищ является целью обзора кода. С другой стороны, написание0 < x < 8
на python имеет очевидное (без пугающих кавычек) значение, что x лежит между 0 и 8, исключая.Во многих языках программирования
x < y
это двоичное выражение, которое принимает два операнда и оценивает один логический результат. Поэтому, если сцепление несколько выражений,true < z
иfalse < z
не будет иметь смысла, и если эти выражения успешно оценить, они , вероятно , чтобы получить неверный результат.Гораздо проще
x < y
представить вызов функции, который принимает два параметра и выдает один логический результат. На самом деле, именно столько языков реализуют это под капотом. Это составное, легко компилируемое, и это просто работает.x < y < z
Сценарий гораздо более сложный. Теперь компилятор, по сути, имеет к моде три функции:x < y
,y < z
, и результат этих двух значений вместе операцию AND, все в контексте , возможно , грамматики неоднозначного языка .Почему они сделали это по-другому? Потому что это однозначная грамматика, намного проще в реализации и намного легче получить правильную.
источник
e1 op1 e2 op2 e3 op3 ...
это эквивалентно, заe1 op e2 and e2 op2 e3 and ...
исключением того, что каждое выражение оценивается только один раз. (a == b is True
re:answer
Вот тут-то и отправился мой разум, чтобы прокомментировать основной вопрос. Я не рассматриваю поддержкуx < y < z
добавления какого-либо конкретного значения в семантику языка.(x < y) && (y < z)
более широко поддерживается, более эксплицитен, более выразителен, более легко переваривается в составные части, более сложен, более логичен, легче реорганизуется.Большинство основных языков (хотя бы частично) объектно-ориентированы. По сути, основной принцип ОО состоит в том, что объекты отправляют сообщения другим объектам (или самим себе), и получатель этого сообщения имеет полный контроль над тем, как реагировать на это сообщение.
Теперь давайте посмотрим, как мы могли бы реализовать что-то вроде
Мы могли бы оценить это строго слева направо (слева-ассоциативно):
Но теперь мы призываем
__lt__
к результатуa.__lt__(b)
, который являетсяBoolean
. Это бессмысленно.Давайте попробуем правоассоциативно:
Нет, это тоже не имеет смысла. Теперь у нас есть
a < (something that's a Boolean)
.Хорошо, а как насчет того, чтобы рассматривать его как синтаксический сахар? Давайте составим цепочку из n
<
сравнений, отправив n-1-иное сообщение. Это может означать, мы посылаем сообщение__lt__
кa
, передаваяb
и вc
качестве аргументов:Хорошо, это работает, но здесь есть странная асимметрия:
a
нужно решить, будет ли она меньшеb
. Ноb
не может решить, будет ли оно меньшеc
, вместо этого это решение также принимаетсяa
.Как насчет того, чтобы интерпретировать это как n-ary сообщение, отправленное на
this
?В заключение! Это может работать. Это означает, однако, что упорядочение объектов больше не является свойством объекта (например,
a
является ли он меньше, чемb
свойствоa
или изb
), а вместо этого является свойством контекста (то естьthis
).С основной точки зрения это кажется странным. Однако, например, в Haskell, это нормально.
Ord
Например, может быть несколько разных реализаций класса типов, и тоa
, меньше или меньшеb
, зависит от того, какой экземпляр класса типов находится в области видимости.Но на самом деле, это не так, что странно на всех! И Java (
Comparator
), и .NET (IComparer
) имеют интерфейсы, которые позволяют вам внедрять свои собственные порядковые отношения, например, в алгоритмы сортировки. Таким образом, они полностью признают, что упорядочение не является чем-то фиксированным для типа, а зависит от контекста.Насколько я знаю, в настоящее время нет языков, которые бы выполняли такой перевод. Однако, есть приоритет: и Ioke, и Seph имеют то, что их конструктор называет «тройными операторами» - операторы, которые синтаксически двоичны, но семантически троичны. Особенно,
это не интерпретируются как отправка сообщения
=
дляa
прохождения вb
качестве аргумента, а также отправка сообщения=
на «текущий Ground» (понятие подобного , но не идентичноеthis
) проходитa
и вb
качестве аргументов. Итак,a = b
интерпретируется каки нет
Это можно легко обобщить на n-арные операторы.
Обратите внимание, что это действительно свойственно языкам OO. В ОО у нас всегда есть один единственный объект, который в конечном итоге отвечает за интерпретацию отправки сообщения, и, как мы видели, для чего-то подобного не сразу очевидно,
a < b < c
какой объект должен быть.Это не относится к процедурным или функциональным языкам. Например, в схеме , Common Lisp , и Clojure , то
<
функция п-арной, и может быть вызвана с произвольным числом аргументов.В частности,
<
делает не средний «меньше», а эти функции интерпретируются несколько иначе:источник
Это просто потому, что дизайнеры не думали об этом или не думали, что это хорошая идея. Python делает это, как вы описали, с простой (почти) LL (1) грамматикой.
источник
1 < 2 < 3
в Java или C #, и у вас нет проблем с приоритетом операторов; у вас проблема с недопустимыми типами. Проблема в том, что он все равно будет анализироваться в точности так, как вы его написали, но вам нужна специальная логика в компиляторе, чтобы превратить ее из последовательности отдельных сравнений в цепочечное сравнение.Следующая программа на C ++ компилируется с nary peep from clang, даже с предупреждениями, установленными на максимально возможный уровень (
-Weverything
):С другой стороны, набор компиляторов gnu предупреждает меня об этом
comparisons like 'X<=Y<=Z' do not have their mathematical meaning [-Wparentheses]
.Ответ прост: обратная совместимость. Существует огромное количество кода, который использует эквивалент
1<3<2
и ожидает, что результат будет истинным.У языкового дизайнера есть только один шанс сделать это «правильным», и это тот момент, когда язык разрабатывается впервые. «Неправильно» изначально означает, что другие программисты довольно быстро воспользуются этим «неправильным» поведением. Если сделать это «правильно» во второй раз, это сломает существующую базу кода.
источник
if x == y is True : ...
Мое мнение: люди, которые пишут такой код, заслуживают того, чтобы подвергнуться каким-то сверхспециальным, необычайным пыткам, которые (если бы он был жив сейчас) сделали бы Торквемаду слабым.