开始F#程序员的常见惊喜是以下事实是一个不完整的匹配:
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
可能是类型的类型,具有"怪异"的比较和平等实现,而这三个分支都不是True。*
但是,在我的describe
函数等情况下,为什么F#编译器不看模式,请说"如果所有when
表达式评估到false
,仍然会有完整的匹配项",然后跳过"不完整的模式匹配"警告?是否有一些特定原因在这里显示此警告,还是仅仅是F#编译器在这里有点简单并给出虚假阳性的警告,因为它的代码不够复杂?
*实际上,可以将x
和y
设置为值,例如x < y
,x = y
和x > y
是 em> ALL all false,而无需超出标准.NET的"正常"边界类型系统。作为一个特殊的奖励问题/难题,这些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的帖子即可。将其视为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"