На многих языках задания легальны в условиях. Я так и не понял причину этого. Зачем писать:
if (var1 = var2) {
...
}
вместо:
var1 = var2;
if (var1) {
...
}
Ответы:
Это более полезно для циклов, чем для операторов if.
while( var = GetNext() )
{
...do something with var
}
Что в противном случае пришлось бы написать
var = GetNext();
while( var )
{
...do something
var = GetNext();
}
Я считаю его наиболее полезным в цепочках действий, которые часто включают обнаружение ошибок и т. Д.
if ((rc = first_check(arg1, arg2)) != 0)
{
report error based on rc
}
else if ((rc = second_check(arg2, arg3)) != 0)
{
report error based on new rc
}
else if ((rc = third_check(arg3, arg4)) != 0)
{
report error based on new rc
}
else
{
do what you really wanted to do
}
Альтернатива (без использования присваивания в условии):
rc = first_check(arg1, arg2);
if (rc != 0)
{
report error based on rc
}
else
{
rc = second_check(arg2, arg3);
if (rc != 0)
{
report error based on new rc
}
else
{
rc = third_check(arg3, arg4);
if (rc != 0)
{
report error based on new rc
}
else
{
do what you really wanted to do
}
}
}
При длительной проверке ошибок альтернатива может запускаться с правой стороны страницы, тогда как версия с назначением в условиях этого не делает.
Проверки ошибок может быть также «действия» - first_action()
, second_action()
, third_action()
- конечно, а не только чеки. То есть это могут быть проверенные шаги в процессе, которым управляет функция. (Чаще всего в коде, с которым я работаю, функции соответствуют строкам проверок предварительных условий или выделению памяти, необходимому для работы функции, или аналогичным строкам).
Это более полезно, если вы вызываете функцию:
if (n = foo())
{
/* foo returned a non-zero value, do something with the return value */
} else {
/* foo returned zero, do something else */
}
Конечно, вы можете просто поставить n = foo (); в отдельном заявлении тогда if (n), но я думаю, что приведенная выше идиома довольно читабельна.
Это может быть полезно, если вы вызываете функцию, которая возвращает либо данные для работы, либо флаг, указывающий на ошибку (или на то, что все готово).
Что-то вроде:
while ((c = getchar()) != EOF) {
// process the character
}
// end of file reached...
Лично мне эта идиома не очень нравится, но иногда альтернатива бывает уродливее.
GCC может помочь вам обнаружить (с -Wall), если вы непреднамеренно попытаетесь использовать присвоение в качестве значения истины, в случае, если он рекомендует вам написать
if ((n = foo())) {
...
}
Т.е. используйте дополнительные скобки, чтобы указать, что это действительно то, что вы хотите.
Идиома более полезна, когда вы пишете while
цикл вместо if
оператора. Что касается if
утверждения, вы можете разбить его, как вы описываете. Но без этой конструкции вам либо пришлось бы повторяться:
c = getchar();
while (c != EOF) {
// ...
c = getchar();
}
или используйте структуру из полутора петель:
while (true) {
c = getchar();
if (c == EOF) break;
// ...
}
Я обычно предпочитаю форму в виде полуторных петель.
while
петли предпочтительнее использовать:do { c = getchar(); ... } while (c != EOF);
for
вместо for (char c = getchar(); c != EOF; c= getchar()) { /* do something with c */ }
этого можно использовать петли: - это самый краткий, и for
стилистически он всегда говорит мне «петля». Небольшой недостаток в том, что вам нужно указать функцию, возвращающуюся c
дважды.
Короткий ответ заключается в том, что языки программирования, ориентированные на выражения, допускают более сжатый код. Они не заставляют вас отделять команды от запросов .
В PHP, например, это полезно для просмотра результатов базы данных SQL:
while ($row = mysql_fetch_assoc($result)) {
// Display row
}
Это выглядит намного лучше, чем:
$row = mysql_fetch_assoc($result);
while ($row) {
// Display row
$row = mysql_fetch_assoc($result);
}
Другое преимущество возникает при использовании gdb. В следующем коде код ошибки неизвестен, если мы выполняем пошаговое выполнение.
while (checkstatus() != -1) {
// process
}
Скорее
while (true) {
int error = checkstatus();
if (error != -1)
// process
else
//fail
}
Теперь во время одного шага мы можем узнать код ошибки возврата из checkstatus ().
Я считаю это очень полезным с функциями, возвращающими необязательные параметры ( boost::optional
или std::optional
в C ++ 17):
std::optional<int> maybe_int(); // function maybe returns an int
if (auto i = maybe_int()) {
use_int(*i);
}
Это уменьшает объем моей переменной, делает код более компактным и не мешает читабельности (я считаю).
То же самое с указателями:
int* ptr_int();
if (int* i = ptr_int()) {
use_int(*i);
}
Причина:
Улучшение производительности (иногда)
Меньше кода (всегда)
Возьмем пример: существует метод, someMethod()
и в if
условии, что вы хотите проверить, равно ли возвращаемое значение метода null
. Если нет, вы собираетесь снова использовать возвращаемое значение.
If(null != someMethod()){
String s = someMethod();
......
//Use s
}
Это снизит производительность, поскольку вы дважды вызываете один и тот же метод. Вместо этого используйте:
String s;
If(null != (s = someMethod())) {
......
//Use s
}