Почему C не имеет оператора логического присваивания?
Мне нужно было закодировать инструкцию формы
a = a || expr;
где expr
должен быть оценен, и результат должен быть назначен a
, если if a
не установлен. это зависит от логических или коротких замыканий.
Более короткий способ написать выше, конечно, будет
a ||= expr;
но (к моему удивлению) C не имеет операторов логического присваивания.
Итак, мой вопрос двоякий. Во-первых, существует более короткий способ написать первое утверждение в стандарте C (тернарный оператор еще хуже - a = a ? a : expr
требует, чтобы я произносил a
трижды).
Во-вторых, почему в C нет логических назначений? Возможные причины, о которых я мог подумать, следующие:
- это усложняет анализ грамматики?
- Есть ли какая-то тонкость в обработке короткого замыкания для этих случаев?
- он считался лишним (но разве это не аргумент против ВСЕХ назначений операторов?)
EDIT
Пожалуйста, откройте этот вопрос, потому что:
-
Вопрос, с которым он был связан (как предполагаемый дубликат), НЕ ОТВЕТИЛ. Ответ на этот вопрос (принятый) гласит, что ||=
отсутствует, поскольку дублирует функциональность |=
. Это неправильный ответ. |=
не замыкается.
-
C и С++ не являются одними и теми же языками. Я хочу знать, почему у C его нет. Фактически, тот факт, что производные языки, такие как С++ и, в частности, Java (которые не страдают от проблем с устаревшим кодом, как это было предложено в ответе Эдмунда), делает вопрос еще более интересным.
РЕДАКТИРОВАТЬ 2
Теперь кажется, что мое первоначальное намерение было неправильным. В заявлении a = a || expr
(где a
является интегралом, а expr
возвращает целочисленное значение, сначала оба a
и expr
будут неявно преобразованы в "booleans", а затем будет присвоено значение "boolean" до a
. Это будет некорректно - интегральное значение будет потеряно. Спасибо, Йенс и Эдмунд.
Итак, для первой части вопроса правильные пути, а не альтернативы:), чтобы закодировать мое намерение, было бы следующим:
if (!a) a = expr;
или
a = a ? a : expr;
они должны быть оптимизированы одинаково (я думаю), хотя лично я предпочел бы первый (потому что он имеет один тип a
).
Однако вторая часть вопроса остается. Аргументы, высказанные Йенсом и Эдмундом о двусмысленности в a ||= expr
, одинаково хорошо применимы к a = a || expr
. случай назначения можно просто рассматривать как нормальный:
- конвертировать
a
в boolean
- если это правда, значение всего выражения становится равным логическому значению
a
- иначе оцениваем
expr
, преобразуем результат в boolean, присваиваем a
и возвращаем его
Вышеприведенные шаги кажутся одинаковыми как для назначения, так и для нормального случая.
Ответы
Ответ 1
a ||= expr
является проблематичным из-за короткого замыкания его эквивалента a = a || expr
.
Чтобы функция a ||= expr
вроде a = a || expr
рассматривала утверждение OP:
"В заявлении a = a || expr
... сначала оба a и expr будут неявно преобразованы в" booleans ","
Это не совсем правильно. expr
не будет преобразован, если a
оценивается до true
. Это будет иметь значение, если expr
будет чем-то вроде scanf()
или rand()
.
Код, например a ||= scanf("%d", &i) != 1;
, будет пытаться сканировать данные с ложным значением в a
. Хотя можно было бы расширить язык таким образом, дополнительные операторы короткого замыкания для текущего набора ||
и &&
скорее всего вызовут больше проблем с кодированием, чем упрощения.
OTOH: быстрый, если запутанный способ записи кода, где функции возвращают ненулевые коды при ошибке.
// Only perform functions until an error occurs.
bool error = foo1();
error &&= foo2();
error &&= foo3();
Ответ 2
Я предполагаю, что простой ответ заключается в том, что ||
является логическим оператором: а в C "логическое" - 0 или 1. Операнды неявно преобразуются в логические значения (я не проверял, что то, что спецификация фактически говорит, но это как C ведет себя), а результат - логический.
Изменение семантики для поддержки этого шаблона вполне возможно - пока кто-то не полагается на ||
делать то, что он всегда делал.
Ответ 3
Поскольку возвращаемый тип операторов ||
и &&
не совпадает с типом их левого аргумента.
Возвращаемый тип ||
и &&
всегда 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 В С++ это bool
.
Ответ 4
Я не могу найти никакой конкретной причины, почему операторы не существуют (на 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 был спроектирован так, как он был разработан.
Если бы этот вопрос касался С++, вы могли бы, например, спросить Бьярна Страустрапа и спросить его, что с ним случилось. Поскольку это не так, мне кажется, что это тупик, потому что стандарт был написан довольно давно, и вы больше не можете спрашивать людей, почему h ***.
С другой стороны, этот неполный операторный набор должен (по-моему) сделать aleardy целым, используя аналогичную запись, чем ваша, поскольку, на мой взгляд, нет оснований против этого.
Надеюсь, я немного помогу.