【问题标题】:Incomplete pattern match when two patterns share a `when` clause当两个模式共享一个“when”子句时,模式匹配不完整
【发布时间】:2017-09-13 07:04:14
【问题描述】:

对于 F# 初学者来说,common surprise 是一个不完全匹配的事实:

let x, y = 5, 10
match something with
| _ when x < y -> "Less than"
| _ when x = y -> "Equal"
| _ when x > y -> "Greater than"

但我刚刚遇到了一个令我惊讶的情况。下面是一小段示例代码来演示它:

type Tree =
| Leaf of int
| Branch of Tree list

let sapling = Branch [Leaf 1]  // Small tree with one leaf
let twoLeafTree = Branch [Leaf 1; Leaf 2]

let describe saplingsGetSpecialTreatment tree =
    match tree with
    | Leaf n
    | Branch [Leaf n] when saplingsGetSpecialTreatment ->
        sprintf "Either a leaf or a sapling containing %d" n
    | Branch subTree ->
        sprintf "Normal tree with sub-tree %A" subTree

describe true sapling // Result: "Either a leaf or a sapling containing 1"
describe false sapling // Result: "Normal tree with sub-tree [Leaf 1]"
describe true twoLeafTree // Result: "Normal tree with sub-tree [Leaf 1; Leaf 2]"
describe false twoLeafTree // Result: "Normal tree with sub-tree [Leaf 1; Leaf 2]"

这个版本的describe 函数产生了“在这个表达式上不完整的模式匹配”警告,即使模式匹配实际上是完整的。没有可能的树不会被该模式匹配匹配,这可以通过删除其中包含when 表达式的匹配的特定分支来看出:

let describe tree =
    match tree with
    | Leaf n -> sprintf "Leaf containing %d" n
    | Branch subTree ->
        sprintf "Normal tree with sub-tree %A" subTree

此版本的describesaplingtwoLeafTree 树返回“普通树”字符串。

match 表达式只包含when 表达式的情况下(例如比较xy 的第一个示例),F# 编译器可能无法判断是合理的比赛是否会完成。毕竟,xy 可能是具有比较和相等的“奇怪”实现的类型,其中这三个分支都不为真。*

但是在像我的 describe 函数这样的情况下,为什么 F# 编译器不查看该模式,说“如果所有 when 表达式评估为 false,仍然会有完全匹配”并跳过“不完整的模式匹配”警告?此处显示此警告是否有某些特定原因,或者只是 F# 编译器在这里有点简单化并给出误报警告,因为它的代码不够复杂?

* 事实上,可以将xy 设置为x &lt; yx = yx &gt; y 的值全部为假,而无需走出去标准 .Net 类型系统的“正常”界限。作为一个特殊的奖励问题/谜题,xy 的这些值是什么?不需要自定义类型来回答这个难题;您所需要的只是标准 .Net 中提供的类型。

【问题讨论】:

    标签: f# pattern-matching guard-clause


    【解决方案1】:

    在 F# match 语法中,when 守卫适用于它之前枚举的所有个案例,而不仅仅是最后一个。

    在您的特定场景中,防护 when saplingsGetSpecialTreatment 适用于 Leaf nBranch [Leaf n] 情况。所以这个匹配在tree = Leaf 42 &amp;&amp; saplingsGetSpecialTreatment = false的情况下会失败

    以下内容将是完整的,因为 Leaf 案例现在有自己的分支:

    let describe saplingsGetSpecialTreatment tree =
        match tree with
        | Leaf n ->
            sprintf "Either a leaf or a sapling containing %d" n
        | Branch [Leaf n] when saplingsGetSpecialTreatment ->
            sprintf "Either a leaf or a sapling containing %d" n
        | Branch subTree ->
            sprintf "Normal tree with sub-tree %A" subTree
    

    【讨论】:

    • 澄清一下,“在它之前枚举”是指没有干预-&gt;
    • 是的,我就是这个意思。
    • 我的原始代码实际上有 Branch 特殊处理的情况 firstLeaf n 情况第二。 Branch 案例有一个 when 条件,但省略了 -&gt; 因为我也想匹配 Leaf 案例。我得到的是错误“模式匹配中的意外符号'|'。预期'-&gt;'或其他令牌。”当时我不明白为什么会失败,但是把Leaf 的案例工作了;现在我知道when 子句适用于该特定“组”中的所有 模式(共享-&gt; 的所有模式),我理解语法错误。谢谢!
    【解决方案2】:

    只是用一个额外的例子来澄清 Fyodor 的帖子。将其视为当 y = 3 部分,否则部分,然后,对于其他所有部分

    let f y x = 
      match x with
      | 0 
      | 1 
      | 2 when y = 3 -> "a"
      | 0
      | 1
      | 2            -> "b"
      | _            -> "c"
    
    [0 .. 3] |> List.map (f 3)
    [0 .. 3] |> List.map (f 2)
    

    FSI

    val f : y:int -> x:int -> string
    
    > val it : string list = ["a"; "a"; "a"; "c"]
    
    > val it : string list = ["b"; "b"; "b"; "c"]
    

    那么,这是一个合理的默认设置吗?我想是这样。

    这是一个更明确的版本:

    let f2 y x =
      match x,y with
      | 0,3
      | 0,3
      | 0,3 -> "a"
      | 0,_
      | 1,_
      | 2,_ -> "b"
      | _ -> "c"
    
    [0 .. 3] |> List.map (f2 3)
    [0 .. 3] |> List.map (f2 2)
    

    ...以及更紧凑的版本:

    let f3 y x = x |> function | 0 | 1 | 2 when y = 3 -> "a"
                               | 0 | 1 | 2 -> "b"
                               | _ -> "c"
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-09-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多