我读了一篇关于函数boolean
参数的有趣文章:
https://medium.com/compileswift/clean-code-the-curse-of-a-boolean-parameter-c237a830b7a3
作者认为,在许多情况下,最好将具有boolean
参数的函数拆分为两个。这是因为使用布尔参数会增加代码的cyclomatic complexity
。
因此,请考虑以下简单函数:
let fbool (b: bool) (x: int) =
if b then x
else -x
接下来,定义从部分应用程序定义的函数,如下所示:
let ftrue x = fbool true x
或等效
let ftrue = fbool true
F#编译器是否足够智能,可以执行作者推荐的内容,即上面定义的ftrue
函数不包含if
语句,因此不会影响圈复杂度?或者它是否包含if
语句,但总是选择true
分支?
答案 0 :(得分:5)
F#编译器不够智能,无法消除这样的分支。一般来说,.NET中的编译器只应用很少的优化,希望抖动能够优化它们。
如果您使fbool
函数内联ftrue
的结果如下:
public static int ftrue(int v)
{
bool flag = true;
if (flag)
{
return v;
}
return -v;
}
这里编译器"显然"应该消除分支,但它没有。
但是,您可能很幸运,代码复杂性工具意识到其中一个分支将永远不会被采用,并且不会将此测试添加到CC分数。
答案 1 :(得分:3)
这篇文章的作者真的在谈论代码是多么容易理解 - 程序员和源代码之间的交互 。当人们谈论圈复杂性时,这几乎总是被考虑的事情。这与编译器的输出无关。
源代码与机器上运行的内容相关的方式是另一回事。虽然分支消除可以提高性能,但它不会从代码中删除分支,因此程序员仍然处理圈复杂度。
如您所述,部分应用函数是在使用时使函数的意图更清晰的好方法。您可以使用有意义的名称来实现两个函数,而不是传递可能无意义的真/假值。这只有在名称比真/假更有意义的情况下才有用,这在他们的玩具示例中并不存在。
但是,这个部分应用程序根本不会降低圈复杂度,因为源代码仍然具有完全相同的分支量。
答案 2 :(得分:2)
我非常确定F#的部分应用程序是以最直接的方式完成的,即不重写函数。要让....
for (var i = 0; i < e.feed.entry.length; i++) {
if(e.feed.entry[i].id.$t.split("post-")[1] === "<data:post.id/>"){
break;
}
for (var j = 0; j < e.feed.entry[i].link.length; j++) {
....
变成一个只返回fbool true
而没有x
语句的函数,并且if
变成一个返回fbool false
的函数,
需要更多的编译器代码而不是它的价值。而且你只能从像这样的琐碎例子中获益,无论如何真的应该分成两个函数。
好吧,也许还有另外一个让你受益的地方:一个单一-x
表达的功能,如:
match
在这种情况下,let f optValue secondParam =
match optValue with
| None -> printfn "No optional param. Second parameter %A" secondParam
| Some x -> printfn "Got an x value of %A, and 2nd param is %A" x secondParam
理论上可以变成一个简单的f None
调用(虽然我不认为编译器会这样做),printfn
会变成另一个f (Some 3)
电话。
然而,即使这个例子也是人为的,也不值得写,因为在大多数函数中你只对一个参数做printfn
,你把那个参数放在 last < / em>(也许使用the function
keyword)。例如,在实际代码中,我上面的示例可能看起来像:
match
在某种情况下,部分应用程序不会导致函数被重写,因为当您应用let f param = function
| None -> printfn "No optional param. Required parameter is %A" param
| Some x -> printfn "Got an x value of %A, and required param is %A" x param
表达式中使用的参数时,您和# 39;已应用最后一个参数,因此您实际上正在调用该函数。
因为现实世界的代码几乎没有从中受益,因此值得添加编译器功能的巨大复杂性(如果在match
语句之前还有其他代码,那么它是真的将它复制成两个函数可能并不重要),我很确定F#编译器没有做你提到的那种重写。
注意:我无法在快速Google搜索中显示任何相关信息,因此这个答案可能不正确;如果是这样,请通过downvote和/或评论通知我,我会对其进行编辑以包含正确的信息。