Багато мов доручення є законними за умов. Я ніколи не розумів причини цього. Чому б ви писали:
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
}
}
}
При тривалій перевірці помилок альтернатива може запустити RHS сторінки, тоді як присвоєння умовної версії цього не робить.
Перевірки помилок може бути також «дії» - 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
}