通过函数调用区分联合模式匹配

时间:2014-01-28 12:11:23

标签: f# pattern-matching discriminated-union

我的问题受到这个问题的启发:link

这是一段代码:

    type A = 
        | X of int * int
        | Y of string

    let f (A.X(a, b)) = a + b 

它有效但有警告: warning

有道理;我和Y没有匹配。

但如果我添加一行

    let f (A.Y(s)) = 10

然后我收到错误:

error

有没有一种很好的方法来修复它并仍然在函数参数中使用模式匹配?如果没有,那么为什么他们会创建这种奇怪的语法,这总会导致警告?

3 个答案:

答案 0 :(得分:8)

您需要对参数进行模式匹配:

let f = function
| X(a, b) -> a + b
| Y(_) -> 10

定义时

let f (A.X(a, b)) = a + b 

f的类型为A -> int,而非A.X -> int。它没有为A.Y的实例值定义,因此您会得到不完整的匹配警告。

您对f的第二个定义也有类型A -> int,因此是第一个的重复定义,因此是错误。如果要在某种联合类型上编写一个总函数,则应使用与functionmatch匹配的模式。

编辑:在回复评论时,如果您想要同时匹配多个参数,可以使用match例如:

let f a1 a2 =
    match (a1, a2) with
    | (X(a, b), X(a', b')) -> a + b
    | (X(a, b), Y(s)) -> a + 10
    | (Y(s), X(a, b)) -> 10
    | (Y(s), Y(s')) -> 20

答案 1 :(得分:4)

某些模式匹配可以是完整的,并且可能在函数参数中有用,例如fst的这个定义是与元组匹配的模式,并且对于所有2个元组都是完整的。

let fst (a,_) = a

其他一些例子:

type Container = Container of string
let unwrap (Container(v)) = v

type Person = { Name:string; Age:int }
let getName {Name=name} = name

答案 2 :(得分:2)

正如其他答案所说,最好的方法似乎是使用functionmatch个关键字进行模式匹配。
但是,F#的原生模式匹配本身就是非常强大的功能。考虑下面的代码,但是我并不主张在现实世界的项目中使用它。我宁愿将它用作练习以更好地理解语言。

let f ((A.X(a, b), _ ) | (A.Y(_), (a, b))) = a + b

// usage
let x1 = f(A.X(10, 42), (100, 1)) // x1 = 52
let x2 = f(A.Y("foo"),  (100, 1)) // x2 = 101

这里发生了什么?

  • 函数的主体计算元组中两个值的总和;
  • 元组 A.X中提取或作为单独的参数提供;
  • 该函数采用额外参数,可以将其视为后备值。也就是说,当第一个参数为A.Y(string)时,我们仍然需要将一些加起来。
  • 如果第一个参数具有A.X(int, int)的某个含义值,则忽略回退值。

然而,不要盲目地在现实世界的项目中使用它,因为它似乎不可读。

进一步阅读:A similar approach for list processing