我刚刚注意到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 ...
来获得我想要的结果,但我刚刚发现了另一种模式匹配的语法,如果我不知道它会永远唠叨我弄清楚如何使用它。
如何使用上面显示的替代模式匹配语法编写函数?
答案 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 :此'或'模式的两边绑定不同的变量集
这是真的;如果是空列表,则hd
和tl
保持未绑定状态。将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
对于具有真正价值的非学术性代码,这更适合使用自我记录标识符。