Стильний склад І / АБО, якщо твердження


13

Як ви створюєте складний склад І / АБО, якщо висловлювання для максимальної читабельності? Як робити відступ і де розміщувати перерви рядка? Моя конкретна ситуація є приблизно такою. Це, безумовно, краще, ніж розбити все в одну лінію, але все одно виглядає безладним.

if (
    (
        x == y
        && a != b
        && p.isGood() 
        && (
            i + u == b
            || q >= a
        )
    )
    || k.isSomething()
    || m > n
) {
    doSomething();
}

1
Чи є навмисним відступ і круглі дужки / структурою дужок цілеспрямовані чи частина стилю?
Ред С.

Смішно. Я задав це те саме питання на SO тиждень тому, і він закрився. Радий бачити це питання десь живим!
Ерік Белар

Відповіді:


6

Створіть булеві змінні для кожного невеликого кроку:

bool step1 = i + u == b || q >= a;
bool step2 = a != b && p.isGood() && group1;
bool step3 = group2 || k.isSomething() || m > n;
if (step3) { doSomething(); }

Звичайно, це схоже на відповідь Лакримології, за винятком різних назв для кожного кроку.

Якщо ви називаєте step1, step2і step3способами, які мають гарний концептуальний сенс, це має бути, безумовно, найбільш розбірливим. p.isGood()а k.isSomething()іноді може викликатись у ситуаціях, коли це не було б у вашому початковому коді, тому це не було б варіантом, якщо ці функції дорогі або якщо ви використовуєте цей код у дуже тісному циклі.

З іншого боку, вам не потрібно турбуватися про ефективність, що може мати місце створення нових змінних; хороший компілятор оптимізує їх.

Приклад виявлення зіткнень прямокутника (який ви, ймовірно, не використовували б із-за вищезазначеного хіта на продуктивність):

if((a.x + a.width >= b.x || b.x + b.width >= a.x)
 && (a.y + a.height >= b.y || b.y + b.width >= a.y)
)
{ collision(); }

Можуть стати:

bool horizMatch = a.x + a.width >= b.x || b.x + b.width >= a.x;
bool vertMatch = a.y + a.height >= b.y || b.y + b.width >= a.y;
if(horizMatch && vertMatch) { collision(); }

Крім того, якщо ви хочете залишити свій код таким, яким він є, я думаю, що це теж було б чудово. Я чесно думаю, що ваш код є досить розбірливим. Очевидно, я не знаю, що саме a b x y i u p k m nє, але що стосується структури, мені це добре виглядає.


8

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


Я б уникнув переломлення в цій ситуації. Було б нерозумно перевіряти такі маленькі функції ізольовано, а наявність визначень, що звисають поза функцією, робить код менш самоочевидним.
Рей Міясака

Це залежить від того, наскільки добре ви рефактор. Я можу стверджувати, що це робить ваш код далеко БІЛЬШЕ самоочевидним, що має значущі назви функцій замість рядка умовних умов, схожий на підручник з алгебри, підкинутий у ваш код.
JohnFx

Однак візуально у вас будуть функції, що звисають назовні, і не буде очевидно, що саме ця функція виконує. Це на мить чорна скринька, поки ви не прокрутите вгору. Якщо ви не володієте мовою, яка дозволяє виконувати функції, я не думаю, що це було б дуже зручно ні для читача, ні для письменника, незалежно від того, наскільки добре ви заломлюєте. І якщо ви знаєте мову, яка дозволяє виконувати функції у функціях, то ймовірність того, що синтаксис навряд чи відрізняється від оголошення оголошень прив'язки чи змінної, наприклад, let x = a > bабо let f a b = a > b.
Рей Міясака

Змінні працювали б однаково добре. Я також вважаю, що це рефакторинг теж.
JohnFx

Ага, гаразд.
Rei Miyasaka

8

Я б робив щось подібне, на цьому рівні складності

bool doIt = x == y && a != b && p.isGood();
doIt &= ( i + u == b || q >= a);
doIt |= k.isSomething() || m > n;

if(doIt)
{
    doSomething();
}

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

З іншого боку, якщо я коли-небудь опиняюсь у ситуації, коли пишуть таке твердження IF, я переосмислюю рішення, оскільки я ВИНАГАЮТЬ, це є спосіб зробити це простіше, або, принаймні, абстрагувати якусь із цієї умови (наприклад: можливо x == y && a != b && p.isGood()насправді просто означає, this->isPolygon()і я можу зробити цей метод;


4

Я з часом все менше одержимий вертикальним вирівнюванням, але моя загальна форма з багаторядковими виразами - це ...

if (   (   (expr1 == expr2)
        || (expr3 == expr4)
        || (expr5 == expr6)
       )
    && (   (expr7 == expr8)
        || (expr9 == expra)
       )
   )
{
  blah;
}

Ключові моменти...

  • Закриті парени вирівнюються вертикально з відкритими паренами, як і з брекетами.
  • Підвиразки, що вміщуються в одному рядку, знаходяться в межах одного рядка і вертикально вирівнюються зліва. Там, де це сприяє читанню, оператори інфікування в цих однорядкових частинах теж вертикально вирівняні.
  • Підтяжні дужки, природно, створюють майже порожні лінії, допомагаючи візуально групувати речі.

Іноді я також відформатую +та *чи інших операторів, як це. Досить декілька складних виразів мають форму суми продукту чи продукту суми (що може стосуватися булевих "сум" та "продуктів"), тому, ймовірно, достатньо загальноприйнято, що послідовний стиль для нього вартий.

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

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

if (   (   (expr1 == expr2)
        || (expr3 == expr4)
        || (expr5 == expr6))

    && (   (expr7 == expr8)
        || (expr9 == expra)))
{
  blah;
}

+1 Мені подобається твій стиль. Він відповів на моє запитання безпосередньо, але я думаю, що Рей Міясака вирізав корінь проблеми. Якщо мені колись лінується зайнятись методом Рей, я скористаюся твою стилізацією.
JoJo

Ого, це справді приємно.
Рей Міясака

1

http://www.codinghorror.com/blog/2006/01/flattening-arrow-code.html

Я погоджуюсь з відповіддю JohnFx, а також одним із Lacrymology. Я б створив купу функцій (бажано статичних), які дозволять досягти невеликих цілей, а потім розвинути їх розумно.

Отже, як щодо чогось подібного? Зауважте, це не ідеальне рішення, але воно працює. Існують способи подальшого очищення, але потрібна більш конкретна інформація. Примітка: цей код повинен працювати так само швидко, оскільки компілятор розумний.

// Currently based on members or global vars
// (which is often a bad idea too)
function doSomethingCondirionally()
{
  if (k.isSomething() || m > n)
  {
    doSomething();
    return;
  }

  // Else ... 
  if (x != y) return;
  if (a == b) return;
  if (!p.isGood()) return;

  // Final, positive check
  if (i + u == b || q >= a)
  {
    doSomething();
  }
}

Якщо мати лише одну точку виходу з функції - це те, що ви цінуєте (наприклад, якщо ви працюєте на функціональній мові), це може бути не найкращим варіантом або навіть доступним. Це сказало, так, саме це я часто роблю.
Рей Міясака

Що робити, якщо це інтерпретована мова як Javascript?
JoJo

@ Rei Miyasaka, скільки я ціную це, залежить від мови. Хоча мені подобається сім'я мов LISP, мені не довелося користуватися нею на роботі. Якщо мені доведеться перефабрикувати чужу функцію, але не торкатися жодного іншого коду (часто це реальність), я б зробив щось подібне вище. Якщо я можу написати / переписати цю логіку з нуля, тоді мій підхід буде іншим, але я не можу написати такий код, не маючи конкретного прикладу того, що автор намагається зробити тут.
Робота

1
@Rei Miyasaka, Ця людина може бути генієм або повна лайно. Я не знаю всього, але мені було б цікаво дізнатися про захист цієї людини щодо точки єдиного виходу. Існує певна дискусія з цього питання і тут, і в мене склалося таке враження, що такий підхід був популярним серед науковців ще в 80-х, можливо, але це вже не має значення і насправді може перешкоджати читанні. Звичайно, якщо ви робите все у функціональному стилі LINQ, тоді ця проблема навіть не виникне.
робота

2
@Job @Steve Я думаю, що це важливіше керівництво в мовах, які потребують явного розселення. Очевидно, не для кожної функції, але, мабуть, це звичка, яку програмістам-початківцям рекомендують дотримуватися, щоб не забути звільнити ресурси.
Рей Міясака

1

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

Дозвольте підкреслити, що ви зробили цю частину правильно: && a != b НІКОЛИ не ставте логічний роз'єм в кінці рядка, це занадто легко пропустити візуально. Ще одне місце, де НІКОЛИ не слід ставити оператора в кінці рядка, - це рядкове конкатенація, мовами з таким оператором.

Зробити це:

String a = b
   + "something"
   + c
   ;

Не робіть цього:

String a = b +
   "something" +
   c;

Чи є у вас логіка чи дослідження, що підтверджують ваше твердження про логічні з'єднувачі, або це лише ваші уподобання, зазначені як факт? Я багато чув про це в різних місцях, і ніколи цього не розумів (на відміну від умовних умов для йоди, які мають поважну (якщо помилково) причину).
Caleb Huitt - cjhuitt

@Caleb - Математика впродовж століть набиралася таким чином. Знімаючи код, ми орієнтуємося на ліву частину кожного рядка. Рядки, що починаються з оператора, очевидно, є продовженням попереднього рядка, а не неправильним відрізком нового оператора.
кевін клайн

Мені також подобається префіксація математичних операторів. Я зрозумів, що це пізно в кар’єрі програмування :)
JoJo

0

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


0

Ви можете розбити код на кілька заяв, що полегшує розуміння. Але справжня ніндзя зробила б щось подібне. :-)

if
(
    (
        x == y
    &&
        a != b
    &&
        p.isGood()
    &&
        (
            i + u == b
        ||
            q >= a
        )
    )
||
    k.isSomething()
||
    m > n
)
{
    doSomething();
}

5
Я шанувальник пробілів, але це надмірно підкреслено майже порожніми лініями на мій смак.
Steve314
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.