当两个模式共享一个“when”子句时,模式匹配不完整
Incomplete pattern match when two patterns share a `when` clause
对于初学 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
函数产生了 "Incomplete pattern matches on this expression" 警告,即使模式匹配实际上是完整的。没有可能的树不会被该模式匹配匹配,正如 删除 匹配的特定分支中有 when
表达式所示:
let describe tree =
match tree with
| Leaf n -> sprintf "Leaf containing %d" n
| Branch subTree ->
sprintf "Normal tree with sub-tree %A" subTree
此版本的 describe
returns sapling
和 twoLeafTree
树的 "Normal tree" 字符串。
在 match
表达式只包含 when
表达式的情况下(就像第一个比较 x
和 y
的例子),这是合理的F# 编译器可能无法判断匹配是否完成。毕竟,x
和 y
可能 是具有 "weird" 比较和相等实现的类型,其中 none 这三个分支是是的。*
但是在像我的 describe
函数这样的情况下,为什么 F# 编译器不查看模式,例如 "If all the when
expressions evaluated to false
, there would still be a complete match" 并跳过 "incomplete pattern matches" 警告?此处出现此警告是否有某些特定原因,或者只是 F# 编译器在这里有点简单化并给出误报警告,因为它的代码不够复杂?
* 事实上,可以将 x
和 y
设置为 x < y
、x = y
和 x > y
的值 all false,从未超出标准 .Net 类型系统的 "normal" 范围。作为特别奖励question/puzzle,x
和y
的这些值是多少?不需要自定义类型来回答这个难题;您所需要的只是标准 .Net 中提供的类型。
在 F# match
语法中,when
守卫适用于 所有 在它之前枚举的情况,而不仅仅是最后一个。
在您的特定情况下,守卫 when saplingsGetSpecialTreatment
适用于 Leaf n
和 Branch [Leaf n]
两种情况。所以这个匹配在 tree = Leaf 42 && 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
只是用一个额外的例子来澄清 Fyodor 的 post。把它想象成一个 when y = 3 部分,一个 otherwise 部分,然后,对于其他一切
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"
对于初学 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
函数产生了 "Incomplete pattern matches on this expression" 警告,即使模式匹配实际上是完整的。没有可能的树不会被该模式匹配匹配,正如 删除 匹配的特定分支中有 when
表达式所示:
let describe tree =
match tree with
| Leaf n -> sprintf "Leaf containing %d" n
| Branch subTree ->
sprintf "Normal tree with sub-tree %A" subTree
此版本的 describe
returns sapling
和 twoLeafTree
树的 "Normal tree" 字符串。
在 match
表达式只包含 when
表达式的情况下(就像第一个比较 x
和 y
的例子),这是合理的F# 编译器可能无法判断匹配是否完成。毕竟,x
和 y
可能 是具有 "weird" 比较和相等实现的类型,其中 none 这三个分支是是的。*
但是在像我的 describe
函数这样的情况下,为什么 F# 编译器不查看模式,例如 "If all the when
expressions evaluated to false
, there would still be a complete match" 并跳过 "incomplete pattern matches" 警告?此处出现此警告是否有某些特定原因,或者只是 F# 编译器在这里有点简单化并给出误报警告,因为它的代码不够复杂?
* 事实上,可以将 x
和 y
设置为 x < y
、x = y
和 x > y
的值 all false,从未超出标准 .Net 类型系统的 "normal" 范围。作为特别奖励question/puzzle,x
和y
的这些值是多少?不需要自定义类型来回答这个难题;您所需要的只是标准 .Net 中提供的类型。
在 F# match
语法中,when
守卫适用于 所有 在它之前枚举的情况,而不仅仅是最后一个。
在您的特定情况下,守卫 when saplingsGetSpecialTreatment
适用于 Leaf n
和 Branch [Leaf n]
两种情况。所以这个匹配在 tree = Leaf 42 && 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
只是用一个额外的例子来澄清 Fyodor 的 post。把它想象成一个 when y = 3 部分,一个 otherwise 部分,然后,对于其他一切
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"