当两个模式共享一个`when`子句时,不完整的模式匹配

rmu*_*unn 4 f# pattern-matching guard-clause

开始F#程序员的一个常见惊喜是以下是一个不完整的匹配:

let x, y = 5, 10
match something with
| _ when x < y -> "Less than"
| _ when x = y -> "Equal"
| _ when x > y -> "Greater than"
Run Code Online (Sandbox Code Playgroud)

但我刚刚遇到一个让我感到惊讶的情况.这里有一小段示例代码来演示它:

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]"
Run Code Online (Sandbox Code Playgroud)

此版本的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
Run Code Online (Sandbox Code Playgroud)

此版本describe返回树saplingtwoLeafTree树的"正常树"字符串.

match表达式只包含表达式的情况下when(比如第一个示例,其中xy正在进行比较),F#编译器可能无法判断匹配是否完整是合理的.毕竟,x并且y 可能是具有比较和平等的"奇怪"实现的类型,其中这三个分支都不是真的.*

但是在像我的describe函数这样的情况下,为什么F#编译器不会看模式,说"如果所有when表达式都被评估false,那么仍然会有完全匹配"并跳过"不完整模式匹配"警告?这个警告是否有一些特定的原因出现在这里,或者仅仅是F#编译器在这里有点过分简单并给出误报警告,因为它的代码不够复杂?

*事实上,它可以设置xy对值,使得x < y,x = y以及x > y所有虚假的,不标准的.NET类型系统的"正常"范围之外的不断加强.作为一个特殊的奖金问题/谜题,这些xy?的价值是什么?没有自定义类型来回答这个难题; 您只需要标准.Net中提供的类型.

Fyo*_*kin 8

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

在您的特定场景中,防护when saplingsGetSpecialTreatment适用于两者Leaf nBranch [Leaf n]案例.因此,这种情况下的匹配将失败tree = Leaf 42 && saplingsGetSpecialTreatment = false

这将是完整的:

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
Run Code Online (Sandbox Code Playgroud)

  • 澄清,通过"在它之前列举",你的意思是没有干预` - >` (3认同)