问题描述
对于刚开始使用F#的程序员来说,常见的惊喜是以下内容不完全匹配的事实:
A common surprise for beginning F# programmers is the fact that the following is an incomplete match:
let x, y = 5, 10
match something with
| _ when x < y -> "Less than"
| _ when x = y -> "Equal"
| _ when x > y -> "Greater than"
但是我刚遇到使我感到惊讶的情况.以下是一些示例代码来演示它:
But I just encountered a situation that surprised me. Here's a small bit of sample code to demonstrate it:
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
表达式的匹配的特定分支,可以看出该模式匹配不会匹配任何可能的树:
This version of the describe
function produced the "Incomplete pattern matches on this expression" warning, even though the pattern match is, in fact, complete. There are no possible trees that will not be matched by that pattern match, as can be seen by removing the specific branch of the match that had a when
expression in it:
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
树的普通树"字符串.
This version of describe
returns the "Normal tree" string for both the sapling
and twoLeafTree
trees.
在match
表达式仅包含when
表达式的情况下(如比较x
和y
的第一个示例),F#编译器可能无法判断是合理的比赛是否完成.毕竟,x
和y
可能是具有比较和相等性的怪异"实现的类型,而这三个分支都不是真的.*
In the case where the match
expression contains nothing but when
expressions (like the first example where x
and y
are being compared), it is reasonable that the F# compiler might not be able to tell whether the match will be complete. After all, x
and y
might be types with a "weird" implementation of comparison and equality where none of those three branches are true.*
但是在类似我的describe
函数的情况下,为什么F#编译器不查看该模式,说如果所有when
表达式的求值为false
,仍然会有完全匹配项",然后跳过不完整的模式匹配"警告?出现此警告是否有某些特定的原因,或者仅仅是F#编译器在这里有点简单化并由于其代码不够复杂而给出错误肯定警告的情况?
But in cases like my describe
function, why doesn't the F# compiler look at the pattern, say "If all the when
expressions evaluated to false
, there would still be a complete match" and skip the "incomplete pattern matches" warning? Is there some specific reason for this warning showing up here, or is it just a case of the F# compiler being just a little bit simplistic here and giving a false-positive warning because its code wasn't sophisticated enough?
*实际上,可以将x
和y
设置为x < y
,x = y
和x > y
为 all 的值,而无需走出屋外标准.Net类型系统的正常"范围.作为特殊的奖励问题/难题,x
和y
的这些值是多少?无需自定义类型即可解决这个难题;您需要的只是标准.Net中提供的类型.
* In fact, it is possible to set x
and y
to values such that x < y
, x = y
, and x > y
are all false, without ever stepping outside the "normal" bounds of the standard .Net type system. As a special bonus question/puzzle, what are these values of x
and y
? No custom types needed to answer this puzzle; all you need is types provided in standard .Net.
推荐答案
在F#match
语法中,when
防护适用于之前列举的所有 all 情况,而不仅仅是最后一种情况一个.
In F# match
syntax, the when
guards apply to all cases enumerated just before it, not just to the last one.
在您的特定情况下,保护when saplingsGetSpecialTreatment
适用于Leaf n
和Branch [Leaf n]
情况.因此,当tree = Leaf 42 && saplingsGetSpecialTreatment = false
In your specific scenario, the guard when saplingsGetSpecialTreatment
applies to both Leaf n
and Branch [Leaf n]
cases. So this match will fail for the case when tree = Leaf 42 && saplingsGetSpecialTreatment = false
以下内容将完成,因为Leaf
案例现在具有其自己的分支:
The following would be complete, since the Leaf
case now has its own branch:
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
这篇关于当两个模式共享一个"when"子句时,模式匹配不完整的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!