Чому ви використовуєте завдання в умові?


80

Багато мов доручення є законними за умов. Я ніколи не розумів причини цього. Чому б ви писали:

if (var1 = var2) {
  ...
}

замість:

var1 = var2;
if (var1) {
  ...
}

Чи справді це питання настільки мовно агностичне? На які мови це впливає? Я знаю, що є C і C ++, що ще?
Вольф

Відповіді:


115

Це корисніше для циклів, ніж оператори if.

while( var = GetNext() )
{
  ...do something with var 
}

Що інакше треба було б написати

var = GetNext();
while( var )
{
 ...do something
 var = GetNext();
}

14
Я згоден - але писав би: while ((var = GetNext ())! = 0) {...}, не думаючи двічі, частково тому, що GCC скаржиться, якщо я цього не роблю з типовими параметрами компіляції.
Джонатан Леффлер

1
Правда. Припускаючи, що ви використовуєте мову, яка підтримує декларації циклу for, і ви ще не використовуєте var за межами циклу.
Джеральд,

1
у деяких мовах, таких як ANSI C, правила масштабу не дозволяють такого підходу, @KerrekSB
wirrbel

7
@wirrbel: Ви маєте на увазі ANSI C з 1989 року? Як коли у них були парові машини та перфокарти? Це може бути так, але чи це актуально?
Kerrek SB

3
Це попередження не тому, що через деякий час із завданням щось не так, а тому, що виконувати завдання, коли ви справді мали намір зробити порівняння, є загальною помилкою. Якщо ви хочете позбутися попередження, ви можете зробити JS, еквівалентний тому, що пропонує @Jonathan Leffler. Особисто я б, мабуть, зробив це в будь-якому випадку через те, наскільки я недовіряю правильним правилам JavaScript :) Це також слід зазначити на деяких мовах, таких як C # та Java, що це єдиний спосіб.
Джеральд

33

Я вважаю це найбільш корисним у ланцюгах дій, які часто передбачають виявлення помилок тощо.

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()- звичайно, а не тільки чеки. Тобто їх можна перевірити кроками в процесі, яким керує функція. (Найчастіше в коді, з яким я працюю, функції перебувають у відповідності до перевірок попередніх умов, виділення пам'яті, необхідних для роботи функції, або подібних рядків).


Так, я насправді зробив саме це, щоб уникнути надмірного вкладеності, а потім здійснив пошук в Інтернеті, щоб зрозуміти, чи це звичайна практика. Врешті-решт я вирішив проти цього, оскільки насправді мав лише ще одну справу, але це, здається, є законною причиною для приведення завдання в стан.
Ібрагім

Співробітники це ненавидять! Для мене, принаймні в більшості випадків, набагато більш ремонтопридатним, ніж гігантське дерево чи багато повернень. Я віддаю перевагу одинарному поверненню з функції ...
Натан

обґрунтований приклад: блоки співпраці роблять зразок очевидним, що само собою пояснюється. Але, добре, внутрішньоблоковий ("псевдо") код виглядає трохи потворно, а зворотний стиль (розміщення того, що ви дійсно хотіли зробити в кінці) не такий вже й чудовий.
Вольф

31

Це корисніше, якщо ви викликаєте функцію:

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), але я думаю, що вищесказане є досить читабельною ідіомою.


3
Думаю, вам слід вкласти його в дужки, інакше gcc скаржиться із: } else {// foo повернув нуль / NULL, зроби щось інше}
Fabio Pozzi

23

Це може бути корисно, якщо ви викликаєте функцію, яка повертає або дані для роботи, або прапор, що вказує на помилку (або що ви закінчили).

Щось на зразок:

while ((c = getchar()) != EOF) {
    // process the character
}

// end of file reached...

Особисто це ідіома, яку я не дуже люблю, але іноді альтернатива є потворнішою.


13

GCC може допомогти вам виявити (за допомогою -Wall), якщо ви ненавмисно намагаєтесь використовувати призначення як значення істини, якщо він рекомендує вам написати

if ((n = foo())) {
   ...
}

Тобто використовуйте додаткові дужки, щоб вказати, що це справді те, що ви хочете.


1
Добре знати. Сподіваюся, ваші співробітники читають її так само.
Вольф

1
Я? Боже, я ніколи не намагався би використовувати призначення як істинне значення. Я мав на увазі те, що рекомендує GCC.
JesperE

GCC також може скомпілювати Fortran . Можливо, бути явним щодо мови програмування?
Пітер Мортенсен,

9

Ідіома є більш корисною, коли ви пишете whileцикл замість ifтвердження. Для ifвисловлення ви можете розбити його, як ви описуєте. Але без цієї конструкції вам або доведеться повторити:

c = getchar();
while (c != EOF) {
    // ...
    c = getchar();
}

або використовуйте циклічну структуру:

while (true) {
    c = getchar();
    if (c == EOF) break;
    // ...
}

Зазвичай я віддав би перевагу формі з половиною циклу.


1
Для whileциклу віддайте перевагу використанню:do { c = getchar(); ... } while (c != EOF);
Scott S

І forзамість цього можна використовувати цикли: for (char c = getchar(); c != EOF; c= getchar()) { /* do something with c */ }- це найбільш стисло і forзавжди стилістично мені каже „петля”. Невеликий мінус полягає в тому, що вам потрібно вказати функцію, яка повертається cдвічі.
Scott S

4

Коротка відповідь полягає в тому, що мови програмування, орієнтовані на вираз, дозволяють отримувати більш стислий код. Вони не змушують вас відокремлювати команди від запитів .


1
Постійна боротьба за примусове кодування без призначення у if (), while () та інших подібних операторах. C / C ++ славиться побічними ефектами, які часто виявляються помилками, особливо коли ми додаємо оператори ++ та -.
Alexis Wilke

1
Перше посилання марне: C або C ++, здається, не така мова.
Вольф

3

Наприклад, у PHP це корисно для перегляду результатів бази даних SQL:

while ($row = mysql_fetch_assoc($result)) {
    // Display row
}

Це виглядає набагато краще, ніж:

$row = mysql_fetch_assoc($result);
while ($row) {
    // Display row
    $row = mysql_fetch_assoc($result);
}

4
Це також має чудовий побічний ефект, коли рядок $ не живе за межами цього циклу і, отже, має право скоріше збирати сміття (мовами, які в будь-якому випадку роблять GC).
Даррен Грівз

2

Інша перевага є під час використання gdb. У наступному коді код помилки невідомий, якщо ми хотіли б зробити один крок.

while (checkstatus() != -1) {
    // process
}

Вірніше

while (true) {
    int error = checkstatus();
    if (error != -1)
        // process
    else
        //fail
}

Тепер під час одного кроку ми можемо знати, яким був код помилки повернення із checkstatus ().


1
Ви маєте на увазі, що це може бути корисним для тимчасових змін під час налагодження коду?
Вольф

0

Я вважаю це дуже корисним для функцій, що повертають додаткові ( 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);
}

До речі, якщо ви хочете обмежити область змінних, ви можете використовувати if з ініціалізаторами :)
cigien

-3

Причиною є:

  1. Покращення продуктивності (іноді)

  2. Менше коду (завжди)

Візьмемо приклад: є метод, someMethod()і в ifстані ви хочете перевірити, чи є повернене значення методу null. Якщо ні, ви збираєтеся знову використовувати значення повернення.

If(null != someMethod()){
    String s = someMethod();
    ......
    //Use s
}

Це буде заважати продуктивності, оскільки ви двічі викликаєте один і той же метод. Замість цього використовуйте:

String s;
If(null != (s = someMethod())) {
    ......
    //Use s
}

Що це за мова? Він не компілюється в C # або C ++ (написання великих літер "Якщо").
Пітер Мортенсен,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.