【问题标题】:Pattern matching warning模式匹配警告
【发布时间】:2020-05-14 14:36:13
【问题描述】:

我在函数中使用模式匹配,如下所示:

let rec calculate   : Calculation<MyType> -> MyVO -> Calculation<MyType list>=
    fun c l ->
        fun arrayA arrayC ->
        myComputationExpression {
            if arrayA.Length<>arrayC.Length then 
                yield! l
            else
                match arrayA, arrayC with
                | [], [] -> yield! C
                | [a], [c] -> yield! calculate a c
                | headA :: tailA, headC :: tailC -> 
                    yield! calculateOpenAny headA headC
                    yield! calculate c l tailA tailC
        }

我有以下警告:

Fsc:C:\myfile.fs(17,27):警告 FS0025:此表达式的模式匹配不完整。例如,值 '( _ , [_] )' 可能表示模式未涵盖的情况。

我完全不明白,因为它不应该因为第一个条件语句而发生:

if arrayA.Length<>arrayC.Length then 

我在这里遗漏了什么,或者我可以忽略警告吗?

【问题讨论】:

    标签: f#


    【解决方案1】:

    问题在于编译器并非无所不知。它可以证明一些东西,但它不能像人类那样完全分析你的程序,关注语义。

    特别是,编译器不知道属性.Length 的值与列表的形状(即是否为空)之间的关系。从编译器的角度来看,Length 只是一个随机属性,你不妨对比一下.ToString() 调用的结果。

    解决此问题的更好方法是在模式匹配中加入不同长度的大小写:

    let rec calculate   : Calculation<MyType> -> MyVO -> Calculation<MyType list>=
        fun c l ->
            fun arrayA arrayC ->
            myComputationExpression {
                match arrayA, arrayC with
                | [], [] -> yield! C
                | [a], [c] -> yield! calculate a c
                | headA :: tailA, headC :: tailC -> 
                    yield! calculateOpenAny headA headC
                    yield! calculate c l tailA tailC
                | _, _ -> yield! l
            }
    

    这里的另一个好处是性能:计算列表的长度实际上是一个 O(n) 操作,所以最好避免它。


    在不相关的注释中,请记住所有这些都是等效的:

    fun a -> fun b -> fun c -> ...
    fun a -> fun b c -> ...
    fun a b -> fun c -> ...
    fun a b c -> ...
    

    这是因为参数在 F#(以及所有 ML 系列)中的工作方式:函数“一个接一个”地接受参数,而不是一次全部接受参数,在每一步返回另一个“预期”其余参数的函数。这称为“currying”。 F# 中的函数默认为 curried

    在您的情况下,这意味着您的函数可以像这样声明更短:

    let rec calculate =
        fun c l arrayA arrayC ->
            ...
    

    甚至更短,像这样:

    let rec calculate c l arrayA arrayC =
       ...
    

    【讨论】:

    • 非常感谢。起初,我像你描述的那样涉及长度,但我认为只有当两个列表具有相同的长度时,遍历列表才会在性能方面更好。你告诉我的是,我应该更好地避免它,并且模式匹配中的长度应该会更好,这很有趣。至于currying,我使用这样的两个函数只是为了美观,但也许这不是函数人所期望的,我现在真的是一个学习者。感谢您冗长的回答。真的很有帮助。
    • 澄清:模式匹配没有长度。在我的代码中,永远不会计算长度。
    • 实际上它已经部分完成,因为您通过 rec 遍历整个列表,直到最后。例如,有两个 10 和 11 项的列表;我们将递归地输入第一个元素,然后是第二个元素,依此类推,直到第十个元素,模式匹配将推导出最后一种情况。
    • 好的,我明白你说的了。使用长度会让我遍历列表两次。
    • 不是两次,而是N+1次(如果两个列表的长度相同)
    猜你喜欢
    • 1970-01-01
    • 2016-05-21
    • 1970-01-01
    • 2018-12-06
    • 1970-01-01
    • 2014-04-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多