Каким должен быть язык программирования? Анализ и критика Описание языка Компилятор
Отечественные разработки Cтатьи на компьютерные темы Компьютерный юмор Новости и прочее

Так ли нужны операции «&&», «||» и «^^»?

В Си-подобных языках принято различать «&&» и «&», «||» и «|», «^^» и «^» («и», «или» и «исключающее или»). Логические операции используют двойные «&&», «||» и «^^», побитовые операции — одинарные.
Причина — в отсутствии полноценного логического типа в родоначальнике этой ветви языков — в языке Си. Остальные языки просто копировали эту «особенность». В Си значение «ложь» — это «0», всё остальное — «истина».

        Возьмём, например, два целых числа, шестнадцатиричные значение которых равно «00000001» и «10000000». Применив к ним логичкую операцию «&&», получаем «истина» && «истина» = «истина». Но применив к ним побитовую «&», получаем: «00000001» & «10000000» = «00000000». Видим, что эти операции не эквивалентны.

        Похоже, что разное обозначение родственных операций всех устраивает. А зря. Если существует логический тип, у которого «ложь» — это «0», а «истина» — не «0» (~0 == 0xFFFFFF, т.е. «-1» в дополнительном коде), то побитовые «и», «или» и «исключающее или» являются и логическими одновременно. Экономия оперций налицо:

int      I, J, K;
         I = 0x00FF0077;
         J = I & 0x11223344;      // результат: 0x00220044
         K = J | 0x00110022;      // результат: 0x00330066
boolean  X = true;
boolean  Y = false;
boolean  Z = X & Y;               // результат: ложь
if ( Z ^ X)                       // результат: истина
        Если сделать так, что оператор «if» принимает только тип boolean, то запись вида «if X=Y» становится незаконной для X и Y любых типов, кроме boolean. Чтобы быть правильной, необходимо делать преобразование к boolean: «if boolean(X=Y)». Это удлинняет запись, но делает очевидными намерения программиста:
int  a, b;
// ...
if (a == b)              // нормально
if (a = b)               // ошибка, если а — не типа boolean
if ((boolean) a = b)     // требуется преобразование к типу boolean
        В логических операций «И» и «ИЛИ» существует один подвох. Дело в том, что они участвуют в ленивых вычислениях. В отличие от побитовых операций. Если первый операнд в операции «И» имеет значение «ложь», а первый операнд операции «ИЛИ» — «истина», то дальнейшие вычисления не производятся. Потому что они бесполезны и уже не могут изменить результата. Но побитовым операциям для вычисления результата второй операнд нужен всегда.

        Но и это не всё. В C++ вычисление второго операнда для логических операций «И» и «ИЛИ» не производится только для предопределённых типов. Для классов, в которых этих операции переопределены, «ленивые вычисления» невозможны. Почему? Да потому, что в языке не существует средств, которыми бы можно было описать, который операнд «ленивый», а который нет.

        От таких вопросов невозможно отмахнуться. Если считать этот момент несущественным и производить вычисление операндов всегда, то результатом может быть не только некоторое замедление вычислений. Дело в том, что если операнд не подставляется, а вычисляется, то это вычисление может иметь побочные эффекты. Ну например, если открыть файл, то система с таким вычислением и без него будет находиться в разных состояниях.

        Но может не всё так трагично, как на первый взгляд? Ведь логические «И» и «ИЛИ» должны, по идее, существовать только для логического типа. Ну может ещё для типов (классов), которые унаследованы от логического Для остальных типов они должны быть побитовыми. Поэтому возможность откладывания вычисления второго операнда нужно оставить только логичесому типу. И типам, для которых логический тип — базовый. Хотя целесообразность создания типов, производных от логического, не очевидна.

        Так что семь раз отмерь, и только один отрежь...

Опубликовано: 2012.09.25, последняя правка: 2018.11.26    17:37

ОценитеОценки посетителей
   ████████████████████████████ 22 (66.6%)
   ██████ 4 (12.1%)
   ██ 1 (3.03%)
   ████████ 6 (18.1%)

Отзывы

     2013/11/16 13:05, Noname          # 

В Форт нет логических && !! ^^ а есть только обычные AND OR XOR
а также False = 0, а True = все остальные значения

     2014/01/16 10:56, Pensulo          # 

Позвольте уточнить и дополнить вас, уважаемый Noname.

В современном стандарте Форт аргументы типа flag (содержащие логическое значение) возвращаемые стандартными словами языка в качестве FALSE должны возвращать одноячеечное число со всеми битами сброшенными в 0, а в качестве TRUE должны возвращать одноячеечное число со всеми битами взведёнными в 1. Так что в знаковом трактовании числа выходит что FALSE = 0, а TRUE = -1.
На практике же, повальное большинство слов принимающих аргумент, расцениваемый как логический (не обязательно типа flag, кстати говоря), проводят анализ на равенство именно FALSE и принимают решения исходя из результата этого сравнения, а потому все прочие числовые значения автоматически воспринимаются равными TRUE.

Но суть вашего сообщения в корне верна, — в Форт нет логических операций AND, OR, XOR, а также операция сравнения "=" всегда расценивает свои аргументы как числовые, а не логические.

     2014/03/18 17:37, Noname          # 

Слова ветвления IF WHILE UNTIL сравнивают аргумент на неравенство нулю и "ответвляются" по данному положению вещей. Пэтому де факто True все ненулевые значения. Хотя слова сравнения = <> и др. возвращают True = -1

     2014/05/23 08:00, Pensulo          # 

Слова ветвления IF WHILE UNTIL сравнивают аргумент на неравенство нулю и "ответвляются" по данному положению вещей.

На деле, при дизассемблировании Форт-кода использующего IF, WHILE и UNTIL, можно видеть что сравнение при ветвлении производится как-раз таки на равенство НУЛЮ.

Поэтому де факто True все ненулевые значения. Хотя слова сравнения = <> и др. возвращают True = -1.

Для операторов ветвления — согласен, так и есть. Но, например, выражение "1 2 AND" даст в результате 0, хотя и 1 и 2 по сути являют собою ненулевые значения (т.е. могут расцениваться как логическое True). То же самое относится и к операторам сравнения = и <>, ибо, как я уже заметил ранее, в Форт нет логических (булевых) операций. Все (ну или почти все) операции рассматривают свои аргументы как сугубо числовые значения. И потому надо проявлять особую осторожность при составлении выражений смешанного типа.

     2014/11/04 05:50, Smart          # 

Оператора ^^ в C\C++ нет, как ни странно. Есть только ^.

Как по мне, запись стоˊит унифицировать, но для логических операций предусмотреть "ленивую" и "неленивую" версии. В C это не предусмотрено, в Паскале поведение логических операторов задаётся опциями компилятора, что неудобно. Самый лучший вариант был в VB.NET: в довесок к "ленивым" And и Or имелись "неленивые" AndAlso и OrElse. То же самое можно предусмотреть и здесь: например &, ! -- неленивые операторы (как для целочисленных, так и для логических величин), а &&, !! -- ленивые (только для логических), или наоборот.

     2014/11/04 10:33, Автор сайта          # 

Оператора ^^ в C\C++ нет, как ни странно. Есть только ^.

Я тоже этому удивлялся.

Мне хотелось бы, чтобы все логические операции были «ленивыми». Разница между «ленивыми» и «неленивыми» вычислениями проявляется тогда, когда функция, возвращающая результат, не является чистой, т.е. производит побочные эффекты. Допустим, f(x) возвращает логическое значение и производит побочные эффекты. Тогда
bool a = false; if (a & f(x)) // . . .
Если «&» — ленива, то f(x) не будет вызвана и не произведёт побочный эффект. Поэтому правильно было бы
bool a = false; bool b = f(x); if (a & b) // . . .
Для упрощения языка я бы всё-таки оставил бы только одинарные «&», «|» и «^», сделав их ленивыми.

     2014/11/07 14:08, Сергей          # 

Оператор ^^ не может иметь ленивых аргументов.

     2014/11/10 19:33, Автор сайта          # 

Ну да, конечно.

     2016/06/22 04:19, rst256          # 

Для упрощения языка я бы всё-таки оставил бы только одинарные «&», «!» и «^», сделав их ленивыми. Программиста, не знающего какие функции у него производят побочные эффекты, все равно уже ничто не спасет. А вот насчет замены операций на побитовые я бы не советовал, будет очень весело, если вдруг туда будет записано неверное значение. Да и как насчет битовых полей и типов меньшего размера? Может

если(а ! б) — логическое
если(а ! б > 0х558) — побитовое
.

     2016/06/22 04:40, rst256          # 

Если сделать так, что оператор «if» принимает только тип boolean, то запись вида «if X=Y» — это что — присваивание или сравнение? Для присвоения вполне допустимо применить неявное приведение к логическому типу (по стандарту Си), ведь неоднозначных операций тут нет. Хотя с таким "нестандартным" значением для true это может быть не лишним. А проверка на равенство по определению должна возвращать значение логического типа, а её аргументами могут быть все типы для которых она определена.

     2016/06/22 14:05, Автор сайта          # 

Возможно, идею с одинаковыми обозначениями для логических и побитовых операций придётся оставить по причине разности в приоритетах. Программисты на Паскале признают, в Си их приоритеты более логичны, меньше путаницы при программировании.

Осутствие автоматического приведения целых типов к логическому убережёт от неправильного понимания «if X=Y». Если Y — целое, то правильным будет «if bool(X=Y)». Значение «true» — это всегда -1, т.е. инверсия нуля («false») устанавливает все биты в «1». А инверсия -1 («true») будет равна 0. Явное приведение целых типов с ненулевым значением к логическому типу даст -1, т.е. «true». Если «false» — это 0, а «true» — это -1, то логические и побитовые операции имеют одинаковую аппаратную реализацию. Несовпадение разрядности приводит к неявному приведению к «наибольшему» типу.

     2016/07/14 10:03, alextretyak          # 

Оператора ^^ в C\C++ нет, как ни странно. Есть только ^.

Я тоже этому удивлялся.

Вообще-то такой оператор есть. :)
Называется: !=
И для булевых типов работает также как ^.
Предполагаю, что авторы языков (Си и его производных) предусмотрительно не стали вводить оператор ^^ с абсолютно таким же действием, как у уже имеющегося оператора.

     2016/07/15 17:25, Автор сайта          # 

Видите ли, в Си есть привычка использовать переменные числового типа как логического: 0 — это «ложь», всё остальное — это «истина». Для числовых типов операция «!=» не эквивалентна гипотетической «^^»:
int a = 1; int b = 2;
a != b // «истина»
a ^^ b // «ложь», т.к. a — «истина» (a=1) и b — «истина» (b=2)
a && b // проверяем: здесь «истина», т.к. a и b тоже «истина»

     2016/07/16 09:50, alextretyak          # 

Ну да, верно, про такой "вариант использования" оператора ^^ я что-то не подумал. ☺
Но, вообще-то говоря, несмотря на кажущееся удобство (не нужно писать "лишнюю" проверку != 0), я считаю такое неявное приведение [int к bool] всё же недопустимым (как и сделано в Rust — там оператор if требует значение строго типа bool).

     2016/07/16 14:48, Автор сайта          # 

Я тоже так считаю. Должно быть явное приведение.

     2016/10/31 03:51, rst256          # 

Отсутствие автоматического приведения целых типов к логическому убережёт от неправильного понимания «if X=Y». Если Y — целое, то правильным будет «if bool(X=Y)».

У меня только один вопрос остается: явное приведение логического типа к логическому же типу (ну так сказать тоже для большей очевидности намерений) будет возможно, т.е. "bool b; ... if bool(b)"?

     2017/01/06 23:48, Автор сайта          # 

На этот счёт такие мысли. В языках со статической типизацией (мы ведь о таком языке размышляем, исходя из философии?) типы известны во время компиляции. Следовательно, компилятор должен обнаружить приведение логического типа к логическому. Это бессмысленная с точки зрения компилятора операция. Она должна быть ликвидирована при оптимизации. В принципе, такие операции можно даже запретить. Тогда программист, увидев ошибку при компиляции А=bool(А), понимает, что масло и так масляное. По идее, стремление программиста перестраховаться операциями типа А=bool(А) говорит о том, что он не может почерпнуть информацию о типе у компилятора, он слишком молчалив. Хорошо бы при компиляции иметь не только исполняемый модуль, но и обзор всех внутренностей программы с подробностями, степень которых можно регулировать.

     2018/11/18 15:26, Freeman          # 

Этюд о битовых и логических операциях недавно восстановлен, став теперь частью документации: «Логические и битовые операции ». Раздельные операции нужны для избавления от скобок.

Ленивость вычислений в Канторе не проблема, в языке с объектами первого класса все операции обобщенные.

     2018/11/26 14:23, Автор сайта          # 

Сперва хотел просто ответить, потом хотел доработать статью. В итоге родилась новая статья: «Изменение приоритетов операций».

     2021/03/28 00:12, Виталий Монастырский          # 

О! Теперь я понял — почему у Вас вызывают такие проблемы всякие там операторы равно и двойного &&. Вы просто не понимаете разницы между этими вещами. То есть, для Вас логические операции и логические операторы — это одно и то же. Для того, чтобы разобраться чем они отличаются, нужно понимать их работу на аппаратном уровне. Как только Вы это поймете, у Вас сразу же все проблемы по этому поводу отпадут сами собой и Вы перестанете видеть на ровном месте сферическую проблему в вакууме.

Логические операторы — сравнивают значения, а логические операции, их лучше ещё называть битовыми, чтобы не блудить в трех соснах — это всего-лишь навсего двоичная однобитовая арифметика. То есть, если true and true = true, то 1000 and 0001 — это никак не false, это именно = 0. Ноль, а не false. Вы просто вычли число из нуля, по сути. То есть, в десятичном представлении это аналогично тому, как если бы Вы пытались сравнить true and true = true с 12 — 12 = 0, и удивлялись тому, что здесь что-то не то.

Логика отдельно, математика отдельно. Битовые операции — это всего-лишь двоичная арифметика, которая отличается от логики ка небо от земли... и лишь обозначение операторов вносит смущение в Ваш разум.

Вот реально представьте себе, что одно — это учебник "Формальной логики", а другое — учебник "Арифметики". Вот и всё.

Как я уже писал, чтобы это лучше различать, для логики разумнее использовать операторы в виде слов — or, xor, and, not, а для побитовой арифметики знаки — &, |, ^, ~. И все дела. В речи для логики применять или, и, не..., а для битовых операций — сумма, разность, умножение, инверсия. Ну, Вы поняли, я думаю. Тогда не заблудитесь. Опять таки добра и самопросвещения!

     2021/03/28 00:57, Автор сайта          # 

О! Теперь я понял

Вы как раз то не поняли. Если Вы с чем-то не согласны, то не стоит чужому мнению приписывать некомпетентность. Это очень лёгкое объяснение. Если бы я пошёл по такому лёгкому пути, то предъявил бы сейчас миру кучу Вашу орфографических и синтаксических ошибок в Ваших сообщениях. А потом бы мог поставить знак равенства между Вашими знаниями в области естественных и искусственных языков.

По теме: я лишь захотел съэкономить на знаках операций. К этому имеются предпосылки в языках со статической типизацией. Если в языке логический тип отличен от целого, если нет автоматического приведения целого к логическому (например, в Си
if (0)
ноль автоматически приводится к логическому типу и равен false), то возможна перегрузка операций «и», «или», «исключающее или». Эти операции, будучи применёнными к значениям логического типа, выполняли бы логические операции. При применении к значениям целого типа, они бы выполняли побитовые операции.

То есть одинаковые знаки операций работали бы по-разному с разными типами данных. Но потом Владислав Джавадов указал мне на то, чему я поначалу не придал значения. У логических и побитовых операций разный приоритет, что может создавать неудобства. Я с этим согласился. В принципе, вопрос можно считать закрытым. Но статья осталась на сайте, как напоминание, что такая идея в принципе существует.

     2021/03/28 10:46, Виталий Монастырский          # 

А... ну тогда понятно. А то сразу было не понятно — что это была Ваша сознательная цель. Может быть я просто читаю Ваши статьи не совсем в порядке очередности по времени, но у Вас так работает обновление страниц, что как напишешь комментарий под одной страницей, оно тебя само перебрасывает куда хочет. Ну и если новая статья, то я сразу же читаю... Так что сложилось впечатление, что это у Вас от недопонимания. А теперь ясно.

НО! Раз Вы занимаетесь разработкой языка, то надо бы как-то по-другому подойти к организации сайта. Если Вы по какому-то вопросу решили сменить свою позицию, то несоответствующие идеи должны перемещаться в архив или каким-либо явным образом помечаться "БОЛЕЕ НЕ АКТУАЛЬНО" или "ИДЕЯ ОТКЛОНЕНА", чтобы вновь прибывающим на Ваш пароход было понятно — на каком этапе у Вас проект, к чему пришли, а что осталось.

Я думаю Вы согласитесь, что это было бы разумно, и сЪэкономило бы другим нервы, а Вам время и не пришлось бы разжевывать каждому — что именно Вы имели в виду.

И по поводу Ваших придирок к моей грамоте и орфографии... да будет Вам известно, даже великие знатоки языка, будучи профессиональными писателями, отдают свои тексты на дополнительную проверку и редактуру специальным людям, которые как раз и занимаются тем, что осматривают текст на предмет этих самых ошибок. А здесь, в комментариях, да ещё под статьями в которых куча банальных опечаток, я лично не вижу смысла вылизывать текст, только бы Вам понравиться. Здесь главное — взаимопонимание по сути, и для этого моего уровня грамотности более чем достаточно. Если Вас смущают мои системные нарушения в области нервной деятельности, то уж простите — была сложная жизнь, иногда и память подводит, и мозги тупят — тут уже ничего не поделаешь, просто приходится компенсировать другими нейронными системами. Можете считать меня инвалидом, если Вам так будет легче. Но должен Вам заявить, что я и в школьные годы не любил и не понимал языков с их неразумными "правилами" и исключениями. Не понимал и презирал за глупость, поэтому никогда не учил. По ним у меня всегда были четверки и это были единственные предметы, по которым у меня даже частенько проскакивали тройки, при этом я даже по этому поводу никогда не заморачивался, потому что никогда не считал "пишы жы шы с буквой и" — наукой.

Ближе к старости в области языкознания я случайно стал увлекаться компаративистикой и потому мое отношение к подобным глупостям только усугубилось, но благодаря различным системам автоматической проверки я все-таки стараюсь писать более принятым образом, хотя и по сей день не считаю это главенствующим и ни к чьей грамотности никогда не придираюсь. Да и не считаю её мерилом интеллекта человека и уж тем более его разумности.

В общем, я думаю, что если Вы серьезно относитесь к своему же собственному проекту, то Вам его нужно немного реорганизовать. За десятилетие существования сайта, думаю это сделать можно. Выделить те концепции, которые на данный момент актуальны, старые статьи удалить в архив для хранения объяснений и выводов, новые переписать в виде новых статей по конкретным темам — тогда всем все сразу будет понятно.

Вот у меня, например — нет такого сайта. Я все делаю скрытно — сам в себе. Но даже у меня есть книга по моему языку, которая изначально пишется в виде популярного и подробного объяснения концепций языка и его синтаксиса. И вот эта книга постоянно обновляется. Даже читая Ваш сайт я уже придумал и нашел целый ряд идей, которыми улучшил синтаксис Cup-а, и сразу же внес изменения, чтобы на глазах всегда была свежая версия. А если все смешать в одну кучу — и коней и людей, то чему тогда удивляться?

Вот такое у меня предложение.

     2021/03/28 19:46, Автор сайта          # 

В языке Ада целый и логический типы не приводятся автоматически друг к другу. В таком случае можно было бы сделать так:
bool a, b, c
. . .
c = a & b // логическая операция "и"
int x, y, z
. . .
z = x & y // побитовая операция "и"
Надо было сразу проиллюстрировать таким примером.

На этом сайте все материалы — стадии «проработка». Когда она закончится, тогда новый язык получит своё название, будет зарегистрировано доменное имя с таким именем. На этом сайте будет только актуальная документация и не будет черновиков. Ну а за этим сайтом, на котором Вы находитесь, оставляю право обладать несовершенством.

     2021/03/28 22:31, Виталий Монастырский          # 

За желанием предельно сократить количество элементов синтаксиса, Вы напрочь забываете про принцип читабельности кода. Вместо
a&b <= c^d or a&c > b|d and a&d != b&c
Вы получите
a&b <= c^d | a&c > b|d & a&d != b&c
и сиди ломай глаза — где тут что и что к чему... А смысл? Ради потери 4 зарезервированных слов для логики? Сокращение базовых элементов языка должно быть разумным — не в нарушение других принципов языка.

Добавить свой отзыв

Написать автору можно на электронную почту mail(аt)compiler.su

Авторизация

Регистрация

Выслать пароль

Карта сайта


Содержание

Каким должен быть язык программирования?

Анализ и критика

●  Устарел ли текст как форма представления программы

●  Русский язык и программирование

●  Многоязыковое программирование

Синтаксис языков программирования

Синтаксический сахар

●  Некоторые «вкусности» Алгол-68

●  «Двухмерный» синтаксис Python

●  Почему языки с синтаксисом Си популярнее языков с синтаксисом Паскаля?

●  Должна ли программа быть удобочитаемой?

●  Стиль языка программирования

●  Тексто-графическое представление программы

●●  Разделители

●●  Строки программы

●●  Слева направо или справа налево?

●  Комментарии

●●  Длинные комментарии

●●  Короткие комментарии

●●  Комментарии автоматической генерации документации

●●  Нерабочий код

●●  Помеченные комментарии

●  Нужны ли беззнаковые целые?

●  Шестнадцатиричные и двоичные константы

●  Условные операторы

●  Переключатель

●  Циклы

●●  Продолжение цикла и выход из него

●  Некошерный «goto»

●  Изменение приоритетов операций

●  Операции присвоения и проверки на равенство. Возможно ли одинаковое обозначение?

●  Так ли нужны операции «&&», «||» и «^^»?

●  Постфиксные инкремент и декремент

●  Почему в PHP для конкатенации строк используется «.»?

●  Указатели и ссылки в C++

●●  Обработка ошибок

Использование памяти

●  Почему динамическое распределение памяти — это плохо

●  Как обеспечить возврат функциями объектов переменной длины?

●●  Типы переменного размера (dynamically sized types, DST) в языке Rust

●●  Массивы переменной длины в C/C++

●●  Размещение объектов в стеке, традиционный подход

●●  Размещение объектов переменной длины с использованием множества стеков

●●  Размещение объектов переменной длины с использованием двух стеков

●●  Реализация двухстековой модели размещения данных

●●  Двухстековая модель: тесты на скорость

●●  Изменение длины объекта в стеке во время исполнения

●●  Размещение объектов переменной длины с использованием одного стека

●  Можно ли забыть о «куче», если объекты переменной длины хранить в стеке

●  Безопасность и размещение объектов переменной длины в стеке

●  Массивы, структуры, типы, классы переменной длины

●  О хранении данных в стеке, вместо заключения

●  Реализация параметрического полиморфизма

Описание языка

Компилятор

Отечественные разработки

Cтатьи на компьютерные темы

Компьютерный юмор

Новости и прочее




Последние отзывы

2021/10/13 20:57 ••• Геннадий Тышов
Электроника без электронщиков

2021/09/11 16:46 ••• Gudleifr
Изобретение очередного велосипеда?

2021/09/01 23:42 ••• Gudleifr
Философия языка

2021/08/31 22:04 ••• Gudleifr
Должна ли программа быть удобочитаемой?

2021/08/30 00:42 ••• Gudleifr
Все языки эквивалентны. Но некоторые из них эквивалентнее других

2021/08/19 20:52 ••• Gudleifr
В защиту PL/1

2021/08/19 20:34 ••• Gudleifr
Каким должен быть язык программирования?

2021/08/11 11:24 ••• Gudleifr
Почему обречён язык Форт

2021/08/07 13:43 ••• Anatoly
Компилятор

2021/08/07 13:30 ••• Anatoly
Многоязыковое программирование

2021/06/16 13:10 ••• Александр Коновалов aka Маздайщик
Не поминайте всуе PL/1

2021/05/19 23:15 ••• Денис Будяк
Энтузиасты-разработчики компиляторов и их проекты

2021/04/25 08:41 ••• kt
Некошерный «goto»

2021/04/19 17:01 ••• Клихальт
О наименовании проекта и языка программирования

2021/04/04 18:29 ••• kt
Переключатель