如何在'let'定义中使用模式匹配?

时间:2009-01-18 17:46:39

标签: syntax f# pattern-matching

我刚刚注意到F#允许我使用带有文字和其他模式的let绑定,如下所示:

let fib 0 = 1
let exists item [] = false
let car (hd :: tl) = hd
let cdr (hd :: tl) = tl

F#正确地将这些函数解释为一种模式匹配,因为我给出了以下警告:

  

警告1不完整的模式匹配   在这个表达。例如,   值'1'不会   匹配

     

警告2不完整的模式匹配   在这个表达。例如,   值'[_]'不会   匹配

     

这些函数按预期工作,但我想用这种样式定义一个完全模式匹配的函数,但是我在F#手册中找不到关于这种替代模式匹配语法的任何内容。

我知道我可以使用let whatever = function ...let whatever x = match x with ...来获得我想要的结果,但我刚刚发现了另一种模式匹配的语法,如果我不知道它会永远唠叨我弄清楚如何使用它。

如何使用上面显示的替代模式匹配语法编写函数?

4 个答案:

答案 0 :(得分:12)

AFAIK,F#中没有办法声明具有相同名称和不同模式匹配签名的多个let绑定。我相信与你正在寻找的最接近的构造是一个函数规则表达式。

以汽车为例

let car = function
    | hd::tl -> hd
    | [] -> failwith "empty list"

答案 1 :(得分:9)

JaredPar是对的,F#没有Haskell在这里做的句法形式。

F#表单主要用于打开单个案例区分联合或用于定义不完整匹配的函数(例如在空列表中失败的'car'示例)。这只是一个事实的结果,实际上语言中的所有名称绑定都是通过模式完成的;由于你所描述的确切原因,这种句法形式(使用参数上的模式定义函数)在实践中通常不太有用。

我认为Haskell在语法形式方面做了很多比ML更好的事情,但F#的根源是ML。好处是有一个很好的F#子集与OCaml交叉编译(它帮助引导了F#语言和用户社区);缺点是F#被一些丑陋/有限的语法“卡住”。

答案 2 :(得分:7)

显然,F#的模式匹配比我们在常规开发中使用的方式更强大。

首先,您可以一次绑定多个值。通常,您将使用List.partition

执行此操作
let data = [7;0;0;0;1;0;1;1;1;1;0;0;1;1;0]
let ones, others = data |> List.partition ((=) 1) // bind two values

作为旁注,您可以将多个标识符绑定到相同的值:

let (a & b) = 42 // a = 42; b = 42

为简单起见,我们先从一个简单的let绑定开始。

let hd::tl = data
  

警告FS0025 :此表达式上的模式不匹配。例如,值“[]”可能表示模式未涵盖的情况。

为了缓解这个问题,我们必须为空列表添加另一个案例:

let (hd::tl) | [] = data
  

错误FS0018 :此'或'模式的两边绑定不同的变量集

这是真的;如果是空列表,则hdtl保持未绑定状态。将tl绑定到相同的空列表很容易:

let (hd::tl) | ([] as tl) = data

但是,错误错误FS0018 不会消失。实际上,我们还必须为hd提供一些“默认”值 以下肮脏的技巧将做到这一点。

let (hd::tl, _) | ([] as tl , hd) = data, 42

上面的行会将hd绑定到data的头部,如果列表不为空,,并在第二个值中提供额外值tuple

注意我还没有找到将42嵌入let构造的方法。

最后,car函数的相同内容:

let car ((hd::tl, _) | ([] as tl, hd)) = hd
let foo = car(data, 42) // foo = 7
let bar = car([], 42)   // bar = 42

答案 3 :(得分:1)

  

如何使用上面显示的替代模式匹配语法编写函数?

正如你所知,但只有当一种模式是详尽的。这个有用的明显例子包括元组和记录以及单例联合和活动模式。

您可以随时使用此语言功能:

let f(a, b) = ...

所以这概括为:

let f(a, (b, c)) = ...

您甚至可以使用它来选择默认值或Some value

let def(x, None | _, Some x) = x
BTW,你建议的样式在Haskell和SML是ML之前在SML中使用,所以这显然与Haskell vs ML无关。我实际上更喜欢OCaml / F#风格,因为它重复性较低:

fun descriptiveFunctionName [] = true
fun descriptiveFunctionName (_::_) = false

VS

let descriptiveFunctionName = function
  | [] -> true
  | _::_ -> false

对于具有真正价值的非学术性代码,这更适合使用自我记录标识符。