Предложение по решению хрупкого сопоставления с образцом - PullRequest
4 голосов
/ 03 декабря 2010

Мне часто нужно сопоставить кортеж значений, которые должны иметь один и тот же конструктор.Обхват _,_ всегда заканчивается в конце.Это, конечно, хрупко, любой дополнительный конструктор, добавленный к типу, будет прекрасно компилироваться.Мои нынешние мысли состоят в том, чтобы иметь совпадения, которые связывают первый, но не второй аргумент.Но есть ли другие варианты?

Например,

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 ]

8 голосов
/ 03 декабря 2010

Вы можете использовать чуть более короткую схему ниже:

| (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: это сопоставление с образцом не исчерпывающий Вот пример значение, которое не соответствует: (символы _, Штаты _)

Теперь вы можете скопировать и вставить предложенный шаблон обратно в ваш код. Обычно так я генерирую не хрупкие универсальные шаблоны для типов с десятками конструкторов. Вам может понадобиться запустить компилятор несколько раз, но это все же быстрее, чем вводить их самостоятельно.

1 голос
/ 03 декабря 2010

Это всего лишь вопрос вкуса / стиля, но я предпочитаю группировать предложения на одном конструкторе, а не собирать сначала полезные предложения для всего, а затем для всех «абсурдных случаев» вместе.Это может быть очень полезно, когда вы пишете несколько «полезных» предложений для одного данного конструктора и хотите проверить, что вы ничего не забыли.

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
1 голос
/ 03 декабря 2010

Это довольно хакерский (и приводит к предупреждению), но вы можете использовать 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

Хотя, к сожалению, это все равно приводит к предупреждению.

...