Цикломатическая сложность логически подобного кода

Рассмотрим следующие три функции, которые ведут себя одинаково, используя разный код для достижения одной и той же цели (примеры написаны на JavaScript, и меня особенно интересуют ответы применительно к JavaScript, но этот вопрос действительно может относиться к любому язык с похожими конструкциями):

// Random number from 0-9
var x = Math.floor(Math.random() * 10);

// JSHint reports a cyclomatic complexity of 3
function a() {
    if (x === 0) {
        return "First";
    } else if (x === 1 || x === 2) {
        return "Second"; 
    }
    return "Third";
}

// JSHint reports a cyclomatic complexity of 4
function b() {
    switch (x) {
    case 0:
        return "First";
    case 1:
    case 2:
        return "Second";
    default:
        return "Third";
    }
}

// JSHint reports a cyclomatic complexity of 1
function c() {
    return x === 0 ? "First" : x === 1 || x === 2 ? "Second" : "Third";
}

// All three functions return the same value
console.log(a(), b(), c());

Инструмент JSComplexity сообщает, что все три функции имеют сложность 4, что означает, что операторы || рассматриваются как независимые ветви, т.е. являются сквозными операторами case. JSHint, похоже, не заботится об операторе ||, но точно так же обрабатывает сквозные операторы case. Кажется, что условный оператор совершенно неверен.

Следует ли при вычислении цикломатической сложности рассматривать сквозные операторы case и логические операторы «или» как независимые ветви? Как насчет тройных условий (я считаю, что это проще, и JSHint в этом случае явно неверен)? Должны ли все три приведенные выше функции иметь одинаковую цикломатическую сложность?


person James Allardice    schedule 18.03.2013    source источник
comment
К вашему сведению, теперь JSHint сообщает, что все три функции имеют сложность 4.   -  person JLRishe    schedule 03.11.2017


Ответы (1)


Цикломатическая сложность — это количество линейно независимых путей в коде. Хотя провал case бесполезен, я считаю, что это однозначно другой путь. Итак, вопрос в том, вводит ли || новую ветку?

Здесь я просто размышляю вслух, но я думаю, что, поскольку JavaScript выполняет ускоренную оценку условных выражений, мы действительно получаем две ветви от дизъюнкции. Например, ваша функция a эквивалентна:

function a() {
    if (x === 0) {
        return "First";
    } else if (x === 1) {
        return "Second"; 
    } else if (x === 2) {
        return "Second";
    } else {
       return "Third";
    }
}

...который имеет 4 ветви, хотя две из них выполняют одну и ту же функцию. (т. е. линейная независимость от ребер, а не от вершин.) Однако, если бы JS не выполнял оценку короткого замыкания, я был бы склонен рассматривать x === 1 || x === 2 просто для вызова одной ветви.

Итак, чтобы ответить на ваш вопрос, я думаю, что все три ваши функции должны иметь одинаковую цикломатическую сложность: 4.

person Xophmeister    schedule 18.03.2013
comment
+1, спасибо, я согласен с этим - все три функции должны иметь одинаковую сложность 4. В JSHint определенно необходимы исправления ошибок. Я немного подожду, прежде чем принять этот ответ, чтобы поощрить дальнейшие ответы, если есть какие-либо другие мнения! - person James Allardice; 18.03.2013