2010-12-03 1 views
4

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

Например,

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 

ответ

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

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

+0

Интересно! Эта стратегия, вероятно, должна быть встроена в интегрированные среды Haskell? – 2010-12-03 15:32:19

+0

Ну, я бы предпочел не иметь всех этих компиляционных предупреждений - дополнительный может затеряться в количестве сдвига, которое будет произведено. На самом деле я понятия не имел, что вы можете делать `или` соответствия внутри кортежей. Спасибо, спасибо. – nlucaroni 2010-12-03 15:33:36

1

Это довольно хакерский (и приводит к предупреждениям), но вы можете использовать Obj, чтобы проверить, равны ли теги или нет. Она должна охватить все случаи, когда и б имеют разные значения:

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 

Предупреждение для неисчерпывающему сопоставления с образцом (так как он не может сказать, соответствует ли или не охранял пункт остальное или нет).

EDIT: вам не нужно использовать Obj на всех, вы можете просто сравнить й и у непосредственно:

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

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

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 

 Смежные вопросы

  • Нет связанных вопросов^_^