@randomguyasking

Как лучше организовать структуру данного кода?

Посоветуйте как лучше организовать данный код. Задача по сути простая:
Дано A, B, C - числовые переменные. Между ними могут быть следующие отношения:
A > B > C
A < B < C
Также дана числовая переменная X. Эта переменная может быть равна A, B, C (3 варианта), может быть в интервалах между ними (2 варианта), и выходить за грани (быть меньше A, или больше C (2 варианта)). Итого 7 вариантов. Учитывая, что может быть как A > B > C так и A < B < C умножаем кол-во вариантов на 2, итого 14 возможных вариантов для X.
Я решил эту задачу только с помощью if'ов:
if (A < B && B < C) {
  if (X < A) { 
    // что-то делаем
  }
  if (X == A) {
    // делаем другое
  }
  if (X > A && X < B) {
    // делаем третье
  }
  // и так далее, все 14 вариантов вхождения для X
}

Этот код работает, но он довольно громоздкий (куча if'ов) и не очень читается (в реальности названия переменных другие и кода больше). Как можно по-другому организовать такой код? Возможно ли здесь подобрать структуру данных, которая сделает код более лаконичным и простым? Или по-другому описать условные проверки?
  • Вопрос задан
  • 165 просмотров
Решения вопроса 2
sergiks
@sergiks Куратор тега Алгоритмы
♬♬
Если действия симметричны, т.е. при X > (C > B > A) действия те же, что при X > (A > B > C),
можно привести переменные к однозначно возрастающему варианту — поменять местами A и C если A > C.
Так вариантов останется 7.
if (A > C) [A, C] = [C, A]; // поменять местами значения A и C
                            // теперь точно A < B < C
if (X < A) {          // вар. 0
} else if (X == A) {  // вар. 1 
} else if (X < B) {   // вар. 2
} else if (X == B) {  // вар. 3
} else if (X < C) {   // вар. 4
} else if (X == C) {  // вар. 5
} else { // X > C     // вар. 6
}


Ещё один способ — с бинарным деревом, который интуитивно но неправильно пытался подсказать xmoonlight
Всего есть 2 * 7 = 14 вариантов. Гипотеза о том, что действия зеркальны для двух вариантов A > B > C и A < B < C вроде бы подтвердилась, поэтому оставлю первый шаг, где меняем местами A и C, чтобы последовательность точно была возрастающей.

Остаётся 7 вариантов. Достаточно 3 битов, чтобы закодировать любой из них. Каждый бит это ответ на 1 вопрос, проверка одного условия. Т.е. достаточно всего трёх if'ов для определения нужного блока кода:
0  1  2  3  4  5  6 
---o-----o-----o---   
   A     B     C      

0  1  0  1  0  1  0  1
0  0  1  1  0  0  1  1
0  0  0  0  1  1  1  1


Примерно так:
if (A > C) [A, C] = [C, A]; // поменять местами значения A и C
                            // теперь точно A < B < C
if (X > B) {
  if (X > C) {    // вар. 6
  } else {
    if (X == C) { // вар. 5
    } else {      // вар. 4
    }
  }
} else {
  if (X > A) {
    if (X == B) { // вар. 3
    } else {      // вар. 2
    }
  } else {
    if (X == A) { // вар. 1
    } else {      // вар. 0
    }
  }
}
Так менее наглядно и читаемо, но сэкономили на спичках – меньше проверок.
Не более 3 проверок условий, чтобы определиться, куда попали.
Ответ написан
Все ваши случаи можно представить себе на координатной прямой и сопоставить каждый из них с числом на этой прямой. Например, если A < B < C, то числа будут положительными, а если A > B > C, то отрицательными (A ближе к нулевой отметке, а C — дальше; чем X дальше от нуля, тем его абсолютное значение больше).

A > B > C > X = -7
A > B > X == C = -6
A > B > X > C = -5
A > X == B > C = -4
A > X > B > C = -3
X == A > B > C = -2
X > A > B > C = -1
0
X < A < B < C = 1
X == A < B < C = 2
A < X < B < C = 3
A < X == B < C = 4
A < B < X < C = 5
A < B < X == C = 6
A < B < C < X = 7

Можно написать функцию, которая определит числовой индекс для каждого из этих случаев. Потом передаёте X, A, B и C в эту функцию, получаете числовой индекс конкретного случая и организуете свой код с помощью switch (case_id). Тогда в нём не будет множества условий сравнения 4 чисел, а только номера случаев, соответствующих разным числам на воображаемой координатной прямой.

Возможно, такая математическая абстракция вам будет нагляднее.

Ещё вариант: возвращать в такой вспомогательной функции не число, а строковый идентификатор вида "A < X < B < C" или "A > X == B > C". Т. е. возвращаемая функцией строка будет выглядеть так, как бы вы написали условие в понятном для себя виде. Свой рабочий код можно затем точно так же организовать средствами switch, только вместо числовых идентификаторов (которые я описал в начале) нужно использовать строковые (с понятными обозначениями условий) — по ним будет легко понять, какой случай в данный момент обрабатывается вашим кодом.

Чтобы избежать опечаток в написании строковых идентификаторов, сохраните их в константы и используйте константы вместо строк — их легко вставлять в код, если в вашем редакторе или IDE есть функция автодополнения.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 2
xmoonlight
@xmoonlight
https://sitecoder.blogspot.com
Скажу сразу, что ответ Сергей Соколов представляет из себя крайне медленное по производительности решение и в целом, его ответ нелогичен и даже ошибочен!
В нём много излишних условий и проверок, которые попросту неверно изначально были им спроектированы логически, что привело к хаосу в его коде.
Он попытался его поправить, используя мой ответ, но, к сожалению, это так и не спасло ситуацию.

Правильно делать так:
/* проверяем все центры */
1. X==A
2. X==B
3. X==C

/* проверяем левую ветку: X<B */
4. X<A
5. else ... //X>A

/* проверяем правую ветку: else */
6. X<C
7. else ... //X>C


При таком подходе затрачиваемое время работы кода на сопоставление X к нужному значению (A,B,C) или диапазону - МИНИМАЛЬНО и ОПТИМАЛЬНО.
Ответ написан
john36allTa
@john36allTa
alien glow of a dirty mind
Если действия нельзя как то сгруппировать по условиям, то if ... else пожалуй лучшая конструкция для js.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы