5

对于 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返回“普通树”字符串。saplingtwoLeafTree

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

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

* 事实上,可以设置xy的值,使x < yx = yx > y都为假,不会超出标准 .Net 类型系统的“正常”界限。x作为一个特殊的奖励问题/谜题,和的这些值是y什么?不需要自定义类型来回答这个难题;您所需要的只是标准.Net 中提供的类型。

4

2 回答 2

9

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

在您的特定情况下,警卫when saplingsGetSpecialTreatment适用于这两种情况。所以这场比赛将失败的情况下Leaf nBranch [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
于 2017-04-17T16:52:27.900 回答
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"
于 2017-04-23T21:56:54.570 回答