Упростить вложенный if / else с повторяющимися результатами? - PullRequest
0 голосов
/ 13 декабря 2018

Я пытаюсь упростить следующее:

function handleDirection(src) {
  if (src === 'left') {
    if (inverse) {
      tracker--;
    } else {
      tracker++;
    }
  } else {
    if (inverse) {
      tracker++;
    } else {
      tracker--;
    }
  }
}

, чтобы уменьшить количество условных выражений.src будет либо 'left', либо 'right' всегда.

Ответы [ 13 ]

0 голосов
/ 28 декабря 2018

Вам не нужно никакого предложения if.Та же операция может быть выполнена путем вычисления положительного или отрицательного приращения в зависимости от src и обратного только с помощью троичного оператора.

function handleDirection(src) {
    tracker += (src == "left" ? 1 : -1) * (inverse ? -1 : 1);
};

Btw.Для эффективности я бы рекомендовал напрямую использовать числовые приращения / убывания вместо строк, которые требуют дополнительной обработки для декодирования.Вы можете использовать константы для достижения той же читабельности:

Также обратное можно оптимизировать как числовое значение, переключаясь между 1 (не инвертировано) и -1 (инвертировано).

const left = 1;
const right = -1;
var direction = 1;

function handleDirection(src) {
    tracker += src * direction;
}

function reverse() { // (Example)
    direction = direction * -1;
}

... даже если «правое» и «левое» ключевое слово происходит из своего рода текстового ввода пользователя, вы можете просто перевести их из словаря:

const steps = {
    left = 1;
    right = -1;
};

function handleDirection(src) {
    tracker += steps[src] * direction;
}
0 голосов
/ 21 марта 2019

Я знаю, что это не является прямым решением вопроса с прямым «упрощением», но я хотел бы предоставить вам ответ, который решает несколько проблем качества кода, а также делает код более читабельным.

О побочных эффектах

Прежде всего эта функция изменяет внешние значения.Это создает проблему побочных эффектов :

  • Функция изменяет внешнее состояние, которое может не обрабатываться во внешней среде, что может привести к неопределенному поведению.
  • Сама функция затем привязывается к внешнему состоянию, что затрудняет изменение и рефакторинг кода.

Также гораздо сложнее протестировать такую ​​функцию, как «создание среды состояний»."сначала для того, чтобы запустить тест.

Первая простая настройка - заставить все внешние значения принимать по параметру и просто вернуть значение 1 или -1, присвоенное какому-либо (вваш случай tracker).

Исключительные или условные выражения со строками

Во-вторых, использование if/else для строковых значений с исключительными или может привести к неопределенному состояниюгде src может быть чем-то иным, чем 'right', но функция ведет себя так, как если бы она была 'right'.Вместо этого следует выбросить исключение.Хорошим подспорьем здесь является использование переключателя.

Применение этих точек к функции

Если учитывать указанные выше точки, общая функция будет выглядеть следующим образом:

function handleDirection (src, inverse) {
  switch (src) {
    case 'left':
      return inverse ? -1 :  1
    case 'right':
      return inverse ?  1 : -1
    default:
      throw new Error(`Unknown src: ${src}`)
  }
}

и вы можете легко протестировать эту функцию:

handleDirection('left' , true)  // -1
handleDirection('left' , false) //  1
handleDirection('right', true)  //  1
handleDirection('right', false) // -1
handleDirection('middle',true)  // Error: Unknown src: middle

Теперь функция явно отделена от tracker (подумайте о вашем ценном времени при рефакторинге), но, кроме того, совершенно ясно, чтофункция делает.

Примечание

Подводя итог, я хотел подчеркнуть, что это не всегда о написании самого простого кода с наименьшим количеством строк, но с кодом ясно читать / понимать и поддерживать.Это не так коротко, как многие из предоставленных решений, но каждый должен немедленно понять, что он делает.

0 голосов
/ 13 декабря 2018

Я не люблю остальных и стараюсь по возможности избегать вложения.Я думаю, что это передает идею inverse более естественным образом:

function handleDirection(src) 
{
    let change = 1;

    if ('right' == src)
        change = -1;

    if (inverse)
        change = -change;

    tracker += change;
}
0 голосов
/ 13 декабря 2018

Вы можете использовать двумерную структуру данных типа массива из js и сохранять желаемые результаты по индексу sec и обратно.Или JSON.

0 голосов
/ 13 декабря 2018

У этого есть только одно условие, и я считаю, что оно читается более интуитивно, чем другие ответы:

function handleDirection(src) {
    if (
        ((src === 'left') && !inverse) ||
        ((src === 'right') && inverse)
    ) {
        tracker++;
    }
    else {
        tracker--;
    }
}
0 голосов
/ 13 декабря 2018

Вы хотите увеличить трекер, если один из src == left или inverse является верным, но не другим, и уменьшить его в противном случае, что и делает оператор "XOR" ^:

function handleDirection(src) {
    if (src === 'left' ^ inverse) {
        tracker++;
    } else {
        tracker--;
    }
}

Вы можете уменьшить это далее, используя троичное выражение:

function handleDirection(src) {
    tracker += src === 'left' ^ inverse ? 1 : -1;
}

Или, если вы хотите избежать каких-либо условных выражений с неявными приведениями и "умной" арифметикой:

function handleDirection(src) {
    tracker += 1 - 2 * (src === 'right' ^ inverse); // either 1-0=1 or 1-2=-1
}
0 голосов
/ 13 декабря 2018

Предполагая, что inverse - это флаг, который вы установили один раз, тогда вам не нужно каждый раз учитывать его, вы можете рассчитать его влияние один раз и просто использовать его как есть,который сократит ваши ветки кода и логику.Если вы хотите изменить его по мере продвижения, вам может понадобиться отделить логику для расчета, чтобы использовать его повторно.

Затем вы можете также извлечь направление движения в автономноеи ваш handleDirection становится очень простым - вы рассчитываете направление, в котором хотите идти, основываясь на src и invert.

let tracker = 0;

//extract logic for the movement offset based on direction
function getDirectionOffset(src) {
  return src === 'left' ? 1 : -1;
}

//have a setter for the invert property
function setInverse(isInverse) {
  movementModifier = isInverse ? -1 : 1
}

//declare the variable dependent on the inverse property
let movementModifier;

//initialise movementModifier variable
setInverse(false);

function handleDirection(src) {
  const offset = getDirectionOffset(src) * movementModifier;
  
  tracker += offset;
}


// usage
setInverse(true);

handleDirection("left");
handleDirection("left");
handleDirection("right");

console.log(tracker);

С учетом всего вышесказанного все это говорит о том, что вам не следует использовать функцию или использовать ее по-другому.Вы можете собрать всю эту функциональность в классе или вместо этого передать всю информацию о функциях, чтобы у вас не было глобальных переменных.Вот примерная объектно-ориентированная реализация концепции:

class TrackerMover {
  constructor(inverse) {
    this.tracker = 0;
    this.movementModifier = inverse ? 1 : -1
  }
  
  handleDirection(src) {
   const offset = this.getDirectionOffset(src) * this.movementModifier;

    this.tracker += offset;
  }
  
  getDirectionOffset(src) {
    return src === 'left' ? -1 : 1;
  }
  
  getPosition() {
    return this.tracker;
  }
}


//usage
const mover = new TrackerMover(true);

mover.handleDirection("left");
mover.handleDirection("left");
mover.handleDirection("right");

console.log(mover.getPosition())

Кстати, другая альтернатива - НЕ вычислять движение каждый раз.Вы на самом деле знаете, что происходит каждый раз - фактически, у вас есть таблица истинности, где ваши входные данные равны src === left и inverse, а выходные - как вы изменили свое отслеживание.

+--------+------------+--------+
| isLeft | isInverted | Offset |
+--------+------------+--------+
| true   | true       |     -1 |
| true   | false      |      1 |
| false  | true       |      1 |
| false  | false      |     -1 |
+--------+------------+--------+

Итак, выможно просто поставить эту таблицу.

let tracker = 0;
let invert = false;

const movementLookupTable = {
  "true": { },
  "false": { },
}

//it can be initialised as part of the above expression but this is more readable
movementLookupTable[true ][true ] = -1;
movementLookupTable[true ][false] = 1;
movementLookupTable[false][true ] = 1;
movementLookupTable[false][false] = -1;

function handleDirection(src) {
  const offset = movementLookupTable[src === "left"][invert];

  tracker += offset;
}


// usage
invert = true;

handleDirection("left");
handleDirection("left");
handleDirection("right");

console.log(tracker);

В этом случае это может быть излишним, но этот подход может быть полезен, если есть больше флагов (в том числе больше значений для флагов) и /или конечные состояния.Например, может быть, вы хотите ввести четыре направления, но не изменяете значение tracker, если оно up или down.

+-----------+------------+--------+
| direction | isInverted | Offset |
+-----------+------------+--------+
| left      | true       |     -1 |
| left      | false      |      1 |
| right     | true       |      1 |
| right     | false      |     -1 |
| up        | false      |      0 |
| up        | true       |      0 |
| down      | false      |      0 |
| down      | true       |      0 |
+-----------+------------+--------+

Как видите, теперь это не просто логическое значение, вы можете обрабатывать любое значение.Используя таблицу, вы также затем изменяете invert на что-то вроде windDirection, поэтому, если движение равно left, а windDirection равно right, результат будет таким же, как сейчас, но вы могли бы получитьнаправление left и ветер left, поэтому вы двигаетесь дальше .Или вы можете двигаться up, а направление ветра - left, поэтому tracker (в этот момент координаты X) будут фактически изменены.

+-----------+---------------+---------+
| direction | windDirection | OffsetX |
+-----------+---------------+---------+
| left      | right         |      -1 |
| left      | up            |       1 |
| left      | down          |       1 |
| left      | left          |       2 |
| right     | up            |      -1 |
| right     | down          |      -1 |
| right     | right         |      -2 |
| right     | left          |       1 |
| up        | up            |       0 |
| up        | down          |       0 |
| up        | left          |       1 |
| up        | right         |      -1 |
| down      | up            |       0 |
| down      | down          |       0 |
| down      | left          |       1 |
| down      | right         |      -1 |
+-----------+---------------+---------+

С четырьмя направлениями и четырьмя направлениями ветрапринять во внимание, что логика может быть довольно раздражающей как для чтения, так и для поддержки в будущем, в то время как если у вас есть только таблица поиска, это легко, и вы можете легко расширить ее даже для обработки диагоналей (предположим, они меняют значение на 0.5 вместо 1) и ваш алгоритм на самом деле не будет беспокоиться, если вы просто получите значения из таблицы.

0 голосов
/ 13 декабря 2018

Вы даже можете сделать это только с одной строкой кода:

function getDirectionOffset(src) {
  tracker += (src === 'left' ? 1 : -1) * (inverse ? -1 : 1);
}
0 голосов
/ 13 декабря 2018

Вы можете использовать синтаксис короткого замыкания или троичные операторы

// by using short circuiting
    function handleDirection(src) {
       if (src == 'left') tracker = inverse && tracker-1 || tracker +1
       else  tracker = inverse && tracker+1 || tracker -1
    }
// by using ternary operator
 function handleDirection(src) {
       if (src == 'left') tracker = inverse ? tracker-1 : tracker +1
       else  tracker = inverse ? tracker+1 : tracker -1
    }
0 голосов
/ 13 декабря 2018

Прямо сейчас вы сравниваете строки, которые я бы не советовал.Например, если вы используете «Left» вместо «left», первый оператор if потерпит неудачу.Возможно, здесь может пригодиться логическое значение, поскольку вы можете гарантировать, что оно имеет только два состояния.

Операторы if внутри могут быть сжаты с помощью условных операторов .

Возможно что-товот то, что вы ищете:

function handleDirection(src) {
  if (src) {
    inverse ? tracker-- : tracker++;
  } else {
    inverse ? tracker++ : tracker--;
  }
}

См .: https://jsfiddle.net/9zr4f3nv/

...