Как рассчитать цикломатическую сложность для R-функций? - PullRequest
14 голосов
/ 12 августа 2011

Цикломатическая сложность измеряет, сколько возможных ветвей можно получить с помощью функции.Существует ли существующая функция / инструмент для ее вычисления для функций R?Если нет, то приветствуются предложения о том, как лучше всего написать один.

Дешевым началом этого будет подсчет всех случаев if, ifelse или switch в вашей функции.Чтобы получить реальный ответ, вам нужно понять, когда начинаются и заканчиваются ветки, что гораздо сложнее.Может быть, некоторые инструменты синтаксического анализа R помогут нам начать работу?

Ответы [ 2 ]

7 голосов
/ 19 августа 2011

Вы можете использовать codetools::walkCode для обхода дерева кодов. К сожалению, документация codetools довольно скудна. Вот объяснение и пример, чтобы вы начали.

walkCode принимает выражение и обходчик кода. Обходчик кода - это созданный вами список, который должен содержать три функции обратного вызова: handler, call и leaf. (Вы можете использовать вспомогательную функцию makeCodeWalker для обеспечения разумной реализации каждого из них по умолчанию.) walkCode обходит дерево кода и по ходу делает вызовы в обходчик кода.

call(e, w) вызывается, когда встречается составное выражение. e является выражением, а w является самим обходчиком кода. Реализация по умолчанию просто возвращается в дочерние узлы выражения (for (ee in as.list(e)) if (!missing(ee)) walkCode(ee, w)).

leaf(e, w) вызывается, когда встречается листовой узел в дереве. Опять же, e - это выражение конечного узла, а w - это обходчик кода. Реализация по умолчанию просто print(e).

handler(v, w) вызывается для каждого составного выражения и может использоваться для простого обеспечения альтернативного поведения call для определенных типов выражений. v - символьное строковое представление родителя составного выражения (немного сложно объяснить - но в основном <-, если это выражение присваивания, {, если это начало блока, if, если это оператор if и т. д.). Если обработчик возвращает NULL, то call вызывается как обычно; если вместо этого вы возвращаете функцию, то она вызывается вместо функции.

Вот чрезвычайно упрощенный пример, в котором подсчитываются случаи появления if и ifelse функции. Надеюсь, это поможет вам начать!

library(codetools)

countBranches <- function(func) {
  count <- 0
  walkCode(body(func), 
           makeCodeWalker(
             handler=function(v, w) {
               if (v == 'if' || v == 'ifelse')
                 count <<- count + 1
               NULL  # allow normal recursion
             },
             leaf=function(e, w) NULL))
  count
}
2 голосов
/ 25 мая 2016

Кроме того, я только что нашел новый пакет под названием cyclocomp (выпущен в 2016 году).Проверьте это!

...