开始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
此版本的describe
会返回sapling
和twoLeafTree
树的“普通树”字符串。
如果match
表达式只包含when
个表达式(如第一个比较x
和y
的示例),则合理的是F#编译器可能无法判断匹配是否完整。毕竟,x
和y
可能是具有“奇怪”的比较和平等实现的类型,其中这三个分支都不是真的。*
但是在像我的describe
函数这样的情况下,为什么F#编译器不会看模式,说“如果所有when
表达式都被评估为false
,那么仍然会有一个完整的匹配“并跳过”不完整的模式匹配“警告?这个警告是否有一些特定的原因出现在这里,或者仅仅是F#编译器在这里有点过分简单并给出误报警告,因为它的代码不够复杂?
*实际上,可以将x
和y
设置为值x < y
,x = y
和x > y
全部 false,没有超出标准.Net类型系统的“正常”范围。作为一个特殊的奖励问题/谜题,x
和y
的这些价值是什么?没有自定义类型来回答这个难题;您只需要标准.Net中提供的类型。
答案 0 :(得分:8)
在F#match
语法中,when
警卫适用于之前枚举的所有个案,而不仅仅是最后一个。
在您的特定情况下,警卫when saplingsGetSpecialTreatment
适用于Leaf n
和Branch [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
答案 1 :(得分:1)
用一个额外的例子澄清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"