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

If statement 编译器如何处理已部分应用的布尔参数?,if-statement,f#,boolean,If Statement,F#,Boolean,我读了一篇关于函数的boolean参数的有趣文章: 作者认为,在许多情况下,最好将具有boolean参数的函数一分为二。这是因为布尔参数的使用增加了代码的圈复杂度 考虑下面的简单函数: let fbool (b: bool) (x: int) = if b then x else -x 接下来,定义从部分应用程序定义的函数,如下所示: let ftrue x = fbool true x 或同等地 let ftrue = fbool true F#编译器是否足够聪明,可以

我读了一篇关于函数的
boolean
参数的有趣文章:

作者认为,在许多情况下,最好将具有
boolean
参数的函数一分为二。这是因为布尔参数的使用增加了代码的圈复杂度

考虑下面的简单函数:

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
分支?

我非常确定F#的部分应用程序是以最简单的方式完成的,即不重写函数。要使
fbool true
变成一个只返回
x
而不使用
if
语句的函数,
fbool false
变成一个返回
-x
的函数 需要的编译器代码超过了它的价值。你只会从像这样的小例子中得到好处,它实际上应该被分成两个函数

好吧,也许还有另外一个地方你会受益:一个函数,它是一个单一的
匹配
表达式,比如:

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(大约3个)
将变成一个不同的
printfn
调用

然而,即使是这个例子也是精心设计的,不太可能值得编写,因为在大多数函数中,您只需对单个参数进行
匹配
,就可以将该参数放在最后(可能还可以使用)。例如,在实际代码中,我上面的示例可能看起来像:

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
在这种情况下,部分应用程序不会导致重写函数,因为当您应用了
匹配表达式中使用的参数时,您已经应用了最后一个参数,因此您实际上正在调用函数

因此,因为现实世界中的代码很难从中获得足够的好处,以至于不值得添加编译器功能的巨大复杂性(如果在
if
语句之前还有其他代码,那么将其复制为两个函数可能是不平凡的),我很确定F#编译器不会进行您提到的那种重写

注意:我无法在谷歌快速搜索中找到这方面的任何信息,因此这个答案可能不正确;如果是这样,请向下投票和/或发表评论通知我,我会编辑它以包含正确的信息。

F#编译器不够聪明,无法消除这样的分支。一般来说,.NET中的编译器只应用很少的优化,希望抖动能够为它们进行优化

如果将
fbool
函数内联
ftrue
设置为如下:

public static int ftrue(int v)
{
    bool flag = true;
    if (flag)
    {
        return v;
    }
    return -v;
}
在这里,编译器“显然”应该消除分支,但事实并非如此


然而,您可能很幸运,代码复杂性工具意识到其中一个分支永远不会被采用,并且不会将此测试添加到CC分数中。

本文作者真正谈论的是代码的可理解性-程序员和源代码之间的交互。这几乎总是人们在谈论圈复杂度时所考虑的。这与编译器的输出无关

源代码与机器上运行的内容之间的关系是另一回事。尽管分支消除可以提高性能,但它不会从代码中删除分支,因此程序员仍然要处理圈复杂度

部分应用您描述的函数是在使用时使函数的意图更清晰的好方法。您没有传递可能毫无意义的真/假值,而是有两个名为的函数。这只有在名称比真/假更有意义时才有用,而你的玩具示例中没有


但是,这个部分应用程序根本不会降低圈复杂度,因为源代码仍然具有完全相同的分支量。

不要盲目地对待圈复杂度!它只是一种用来量化人们与代码复杂性相关联的代码属性的度量方法(即,通过流控制结构(如if-then-else或match)进行分支的数量)。如果您正在建模的逻辑中存在真正的复杂性,那么无论如何它都需要在代码中以某种方式显现出来。@scrwtp-假设我有一个非常复杂的函数,包含许多行代码。某处有一个
if
语句。假设我有两个完全独立的项目,一个使用
true
版本,另一个使用
false版本
@scrwtp——假设我有一个包含许多代码行的非常复杂的函数。某处有一个
if
语句。假设我有两个完全独立的项目,一个使用
true
版本,另一个使用
false
版本。在这两个项目中,布尔值都没有关联的复杂性。通过使用上述部分应用程序,我可以避免编写两个不同函数所涉及的代码重复。如果以后我找到更好的方法来实现该功能,两个版本将同时更新。这种推理通常适用于部分应用程序,而不仅仅是当参数为boolean时。对不起,上面的注释不完整。5分钟的限制让我明白了。我明白了你问题的关键(即编译器是否试图优化东西)