作为Graham扫描的一部分,我正在编写一个函数,用于确定直线在某个点是向左转弯还是向右转弯。考虑到我们有一个函数determinant point -> point -> point -> int
,其中determinant p0 p1 p2
返回det(p0p1,p0p2(,这里是代码:
(* Determines how the line turns at s, when going from t to p*)
let turn t p s =
let det = det s p t in
if det < 0 then
'r' (* Turning right *)
else if det > 0 then
'l'(* Turning left *)
else
's' (* Going straight ahead *)
;;
有没有什么方法可以用模式匹配来代替呢?以下是我尝试过的,但显然不起作用:
let turn t p s =
match det s p t with
| < 0 -> 'r'
| 0 -> 's'
| > 0 -> 'l'
;;
(注意,变量/函数名称由类决定;虽然我确实发现单个字母的名称有点令人困惑,但我可能不会更改它们…(
否。
嗯
有条件的模式保护可以按照你的建议进行。
let turn t p s =
let det = det s p t in
if det < 0 then
'r' (* Turning right *)
else if det > 0 then
'l'(* Turning left *)
else
's' (* Going straight ahead *)'
将变成:
let turn t p s =
match det s p t with
| n when n < 0 -> 'r'
| n when n > 0 -> 'l'
| _ -> 's'
其中哪一个更清楚地表达了正在发生的事情,这是一个意见问题。
那…呢
随着更复杂的数据的匹配,这个论点变得更有趣。下面是一个非常做作的例子,它的实际实现要好得多。
type num = Int of int | Float of float
let rec cmp a b =
match a, b with
| Int a, Int b ->
if a < b then -1
else if a > b then 1
else 0
| Float a, Float b ->
if a < b then -1
else a > b then 1
else 0
| Int a, Float b ->
let a = float_of_int a in
if a < b then -1
else a > b then 1
else 0
| Float a, Int b ->
let b = float_of_int b in
if a < b then -1
else a > b then 1
else 0
使用条件保护:
let rec cmp a b =
match a, b with
| Int a, Int b when a < b -> -1
| Int a, Int b when a > b -> 1
| Int a, Int b -> 0
| Float a, Float b when a < b -> -1
| Float a, Float b when a > b -> 1
| Float a, Float b -> 0
| Int a, Float b when float_of_int a < b -> -1
| Int a, Float b when float_of_int a > b -> 1
| Int a, Float b -> 0
| Float a, Int b when a < float_of_int b -> -1
| Float a, Int b when a > float_of_int b -> 1
| _ -> 0
这仍然是一个意见问题,但有条件的警卫可以帮助";"压平";模式匹配中的嵌套条件。
目前只有char
(OCaml 5.0之前(支持整数范围。即使是char
,实际上也支持部分范围:模式'a'..'d'
只不过是'a'|'b'|'c'|'d'
的语法糖。但这种编码只能用于像char这样的小整数类型:将区间0..int_max
分解为int_max
模式对于63位整数来说不是一种选择。
在模式匹配编译器中支持完整的整数范围(或完全有序基元类型上的范围(是可能的,但这将需要大量尚未完成的工作。