解决脆弱模式匹配的建议

时间:2010-12-03 15:16:35

标签: functional-programming pattern-matching ocaml

我经常需要匹配应该具有相同构造函数的值元组。 cat _,_总是在最后结束。这当然是脆弱的,添加到该类型的任何其他构造函数将完全编译。我目前的想法是连接第一个但不是第二个参数的匹配。但是,还有其他选择吗?

例如,

type data = | States of int array 
            | Chars  of (char list) array

let median a b = match a,b with
    | States xs, States ys ->
        assert( (Array.length xs) = (Array.length ys) );
        States (Array.init (Array.length xs) (fun i -> xs.(i) lor ys.(i)))
    | Chars xs, Chars ys -> 
        assert( (Array.length xs) = (Array.length ys) );
        let union c1 c2 = (List.filter (fun x -> not (List.mem x c2)) c1) @ c2 in
        Chars (Array.init (Array.length xs) (fun i -> union xs.(i) ys.(i)))
    (* inconsistent pairs of matching *)
    | Chars  _, _
    | States _, _ -> assert false

3 个答案:

答案 0 :(得分:8)

您可以使用下面稍短的图案:

| (Chars _| States _), _ -> assert false

事实上,您可以让编译器为您生成它,因为它仍然有点乏味。 输入以下内容并编译:

let median a b = match a,b with
| States xs, States ys ->
    assert( (Array.length xs) = (Array.length ys) );
    States (Array.init (Array.length xs) (fun i -> xs.(i) lor ys.(i)))
| Chars xs, Chars ys -> 
    assert( (Array.length xs) = (Array.length ys) );
    let union c1 c2 = (List.filter (fun x -> not (List.mem x c2)) c1) @ c2 in
    Chars (Array.init (Array.length xs) (fun i -> union xs.(i) ys.(i)))
  

警告8:此模式匹配是   并非详尽无遗。这是一个例子   一个不匹配的值:( Chars _,   国家_)

现在,您可以将建议的模式复制粘贴回代码中。这通常是我为具有数十个构造函数的类型生成非脆弱的catch-all模式的方法。您可能需要多次启动编译器,但它仍然比自己键入它们更快。

答案 1 :(得分:1)

这非常hackish(并导致警告),但您可以使用Obj来检查标签是否相等。它应该捕获a和b具有不同值的所有情况:

type data = | States of int array 
            | Chars  of (char list) array

let median a b = match a,b with
    | States xs, States ys ->
        assert( (Array.length xs) = (Array.length ys) );
        States (Array.init (Array.length xs) (fun i -> xs.(i) lor ys.(i)))
    | Chars xs, Chars ys -> 
        assert( (Array.length xs) = (Array.length ys) );
        let union c1 c2 = (List.filter (fun x -> not (List.mem x c2)) c1) @ c2 in
        Chars (Array.init (Array.length xs) (fun i -> union xs.(i) ys.(i)))
    (* inconsistent pairs of matching *)
    | x, y when (Obj.tag (Obj.repr x)) <> (Obj.tag (Obj.repr y)) -> assert false

警告是针对非详尽的模式匹配(因为它无法判断被保护的条款是否与其余条款匹配)。

编辑:您根本不需要使用Obj,您可以直接比较x和y:

| x, y when x <> y -> assert false

不幸的是,这仍会导致警告。

答案 2 :(得分:1)

这只是品味/风格的问题,但我更倾向于将同一个构造函数的子句分组在一起,而不是先将所有条件都包含在内,然后将所有“荒谬的案例”放在一起。当你为一个给定的构造函数编写几个“有用的”子句时,这非常有用,并且想要检查你没有忘记任何事情。

let median a b = match a,b with
  | States xs, States ys ->
    assert( (Array.length xs) = (Array.length ys) );
    States (Array.init (Array.length xs) (fun i -> xs.(i) lor ys.(i)))
  | States _, _ -> assert false

  | Chars xs, Chars ys -> 
    assert( (Array.length xs) = (Array.length ys) );
    let union c1 c2 = (List.filter (fun x -> not (List.mem x c2)) c1) @ c2 in
    Chars (Array.init (Array.length xs) (fun i -> union xs.(i) ys.(i)))
  | Chars _, _ -> assert false