圈复杂度衡量通过一个函数可以采用多少个可能的分支。是否有现有的函数/工具来计算 R 函数?如果没有,建议以最佳方式编写。
一个廉价的开始是计算所有出现的if
,ifelse
或switch
在你的函数中。但是,要获得真正的答案,您需要了解分支何时开始和结束,这要困难得多。也许一些 R 解析工具会让我们开始?
圈复杂度衡量通过一个函数可以采用多少个可能的分支。是否有现有的函数/工具来计算 R 函数?如果没有,建议以最佳方式编写。
一个廉价的开始是计算所有出现的if
,ifelse
或switch
在你的函数中。但是,要获得真正的答案,您需要了解分支何时开始和结束,这要困难得多。也许一些 R 解析工具会让我们开始?
您可以使用codetools::walkCode
遍历代码树。不幸的是,codetools 的文档非常稀少。这是一个解释和示例,可帮助您入门。
walkCode
接受一个表达式和一个代码步行者。Code walker 是您创建的列表,它必须包含三个回调函数:handler
、call
和leaf
。(您可以使用 helper 函数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
}
另外,我刚刚发现了一个名为 cyclocomp 的新包(2016 年发布)。看看这个!