编译器如何处理已部分应用的布尔参数?

时间:2017-07-27 01:52:26

标签: if-statement f# boolean

我读了一篇关于函数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分支?

3 个答案:

答案 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和/或评论通知我,我会对其进行编辑以包含正确的信息。