Почему в C нет логического оператора присваивания? - PullRequest
31 голосов
/ 17 августа 2010

Мне нужно было закодировать оператор в форме

a = a || expr;

, где expr должно быть оценено, и результат должен быть присвоен a, если a не установлено. это зависит от возможностей логического ИЛИ по короткому замыканию.

Короче, как написано выше, конечно, будет

a ||= expr;

но (к моему удивлению) C не имеет логических операторов присваивания.

Так что мой вопрос двоякий. Во-первых, есть ли более короткий способ написать первое утверждение в стандартном C (троичный оператор еще хуже - a = a ? a : expr требует, чтобы я прописал a трижды).

Во-вторых, почему в C нет логических назначений? Возможные причины, о которых я мог подумать:

  • это затрудняет анализ грамматики?
  • есть некоторая тонкость в обработке короткого замыкания для этих случаев?
  • это считалось излишним (но разве это не аргумент против ВСЕХ назначений операторов?)

РЕДАКТИРОВАТЬ

Пожалуйста, разблокируйте этот вопрос, потому что:

  • Вопрос, с которым он был связан (как предполагаемый дубликат), НЕ БЫЛ ОТВЕТЕН. В (принятом) ответе на этот вопрос говорится, что ||= отсутствует, поскольку дублирует функциональность |=. Это неправильный ответ. |= не закорачивает.

  • C и C ++ НЕ являются одинаковыми языками. Я хочу знать, почему у С этого нет. Фактически, тот факт, что производные языки, такие как C ++ и, в частности, Java (который не страдал от проблем унаследованного кода, как было предложено в ответе Эдмунда), делает вопрос еще более интересным.

РЕДАКТИРОВАТЬ 2

Теперь кажется, что мое первоначальное намерение было неверным. В операторе a = a || expr (где a является целым, а expr возвращает целое значение, сначала оба значения a и expr будут неявно преобразованы в «логические значения», а затем будет назначено «логическое» значение a. Это будет неверно - интегральное значение будет потеряно. Спасибо, Дженс и Эдмунд.

Так что для первой части вопроса, правильные пути, а не альтернативы :), кодировать мое намерение было бы:

if (!a) a = expr;

или

a = a ? a : expr;

они должны быть оптимизированы так же (я думаю), хотя лично я предпочел бы первый (потому что он имеет на один меньше a для ввода).

Однако вторая часть вопроса все еще остается. Аргументы, высказанные Йенсом и Эдмундом о двусмысленности в a ||= expr, в равной степени применимы и к a = a || expr. случай назначения можно просто рассматривать как нормальный:

  • конвертировать a в логическое значение
  • если оно истинно, значение всего выражения становится равным логическому значению a
  • в противном случае оцените expr, преобразуйте результат в логическое значение, присвойте a и верните его

Шаги, описанные выше, кажутся одинаковыми как для назначения, так и для обычного случая.

Ответы [ 4 ]

5 голосов
/ 15 июля 2015

a ||= expr проблематично из-за оценки его короткого замыкания на эквивалент a = a || expr.

Чтобы a ||= expr функционировал как a = a || expr, учитывайте утверждение ОП:

"В операторе a = a || expr ... сначала и a, и expr будут неявно преобразованы в" booleans ","

Это не совсем правильно. expr не будет преобразовано, если a оценивается как true. Это будет иметь значение, если expr будет что-то вроде scanf() или rand() или какой-либо функцией, которая влияет на состояние программы.

Код, такой как a ||= scanf("%d", &i) != 1;, будет пытаться сканировать данные только с ложным значением в a. Хотя было бы возможно расширить язык таким способом, дополнительные операторы короткого замыкания для текущего набора || и &&, вероятно, вызовут больше проблем кодирования, чем явные упрощения.

С другой стороны: быстрый, хотя и запутанный, способ написания кода, когда функции возвращают ненулевые коды при ошибке.

// Perform functions until an error occurs.
bool error = foo1();
error &&= foo2();  // Only valid if C was extended with &&=
error &&= foo3();
3 голосов
/ 26 февраля 2015

Я не могу найти какую-либо конкретную причину, почему операторы не существуют (в C99).

Таким образом, единственная причина, которую я могу найти, состоит в том, что в C89 не было логического типа, и эти логические операторы предназначались для использования исключительно в if.

Пример:

int i = 5;

/* This should not make any difference,
   since or'ing with false, shouldn't change
   the value... dib di dib diddy...*/
i ||= 0; /* Actually: i = i || 0, which gives 'true' */

i теперь «1», что для большинства людей довольно противоречиво.

Этот оператор, очевидно, не принесет никакого улучшения ясности или кодирования без логического типа, который бы создавал смысл или был бы с другим.

По моему мнению, реализация a ||= b; как if(!a) a = b; была бы довольно простой и уже была реализована, например, например. Lua.

Так что ваш вопрос, кажется, немного, почему C был разработан так, как он был спроектирован. Если бы этот вопрос был о C ++, вы могли бы, например, спросить Бьярна Страуструпа и спросить его, что на него нашло. Поскольку это не так, мне кажется, что это тупик, потому что стандарт был написан довольно давно, и вы больше не можете спрашивать людей, почему h ***.

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

Я надеюсь, что смогу немного помочь.

3 голосов
/ 17 августа 2010

Полагаю, простой ответ заключается в том, что || является логическим оператором: а в C "логическое значение" равно 0 или 1. Операнды неявно преобразуются в логические значения (я не проверял, что именно так и сказано в спецификации)., но это то, как ведет себя C), и в результате получается логическое значение.

Изменение семантики для поддержки этого шаблона вполне может быть осуществимо - пока кто-то не будет полагаться на ||, делая то, что всегда делает.

2 голосов
/ 16 апреля 2014

Поскольку тип возвращаемых операторов || и && не совпадает с типом их левого аргумента.

Тип возвращаемого значения || и && всегда int 1 , в то время как левый аргумент может быть любым целым, с плавающей точкой или указателем.Операнды также не обязательно должны быть одного типа.Следовательно, определение x ||= y в качестве x = x || y и x &&= y в качестве x = x && y в соответствии с другими расширенными присваиваниями не сможет сохранить результат в аргументе для большинства типов.

Вы можете прийтис другими определениями, например x ||= y как if(!x) x = y и x &&= y как if(!y) x = y, но это не совсем очевидно, и это не , что полезно, поэтому оно не было включено.

1 В C ++ это bool.

...