Как определить функцию индуктивно по двум аргументам в Coq?

14

Как я могу убедить Coq, что приведенная ниже рекурсивная функция завершается? Функция принимает два индуктивных аргумента. Интуитивно понятно, что рекурсия завершается, потому что любой аргумент разлагается.

В частности, функция принимает два дерева в качестве входных данных.

Inductive Tree :=
| Tip: Tree
| Bin: Tree -> Tree -> Tree.

На деревьях мне нравится делать следующий стиль индукции.

Inductive TreePair :=
| TipTip : TreePair
| TipBin : Tree -> Tree -> TreePair
| BinTip : Tree -> Tree -> TreePair
| BinBin : TreePair -> TreePair -> TreePair.

Fixpoint pair (l r: Tree): TreePair :=
  match l with
    | Tip =>
      match r with
        | Tip => TipTip
        | Bin rl rr => TipBin rl rr
      end
    | Bin ll lr =>
      match r with
        | Tip => BinTip ll lr
        | Bin rl rr => BinBin (pair l rl) (pair lr r)
      end
  end.

Определение TreePair принято, но определение пары функций выдает сообщение об ошибке:

Error: Cannot guess decreasing argument of fix.

Поэтому меня интересует, как убедить Coq в расторжении.

yhirai
источник
1
Вы пробовали передавать l и r вместе как продукт, а не использовать карри? Это должно помочь ему в этом.
Per Vognsen
1
Некоторые люди думают, что этот вопрос касается программирования и выходит за рамки этого сайта. Хотя я не уверен, что я согласен, вы можете узнать о потенциальной проблеме. Если кому-то есть что сказать по поводу уместности, напишите, пожалуйста, о мета-дискуссии, на которую я ссылался.
Цуёси Ито
3
Этот вопрос действительно касается указания монотонно убывающих границ для структур данных, чтобы гарантировать, что операция pairчетко определена. Coq это просто транспортное средство.
Дэйв Кларк

Ответы:

12

Определения фиксированной точки Coq требуют, чтобы индуктивные вызовы получали структурно меньший аргумент. В глубине души конструкция с фиксированной точкой принимает один аргумент: нет встроенной концепции рекурсивного определения для двух аргументов. К счастью, определение Coq структурно меньшего размера включает типы высшего порядка, что является чрезвычайно мощным.

Ваше определение точки фиксации с двумя аргументами следует простому шаблону: либо первый аргумент становится меньше, либо первый аргумент остается идентичным, а второй аргумент становится меньше. Этот довольно распространенный шаблон может быть обработан простым исправлением.

Fixpoint pair l := fix pair1 (r : Tree) :=
  match l with
    | Tip => match r with
              | Tip => TipTip
              | Bin rl rr => TipBin rl rr
            end
    | Bin ll lr => match r with
                    | Tip => BinTip ll lr
                    | Bin rl rr => BinBin (pair1 rl) (pair lr r)
                   end
  end.

Для более сложных случаев, или если ваши вкусы работают таким образом, вы можете использовать рекурсию ближе к тому, как она преподается на курсах по математике, создавая точку фиксации из пошагового вычисления и отдельного аргумента обоснованности , часто используя целочисленную меру . Вы также можете сделать свое определение более похожим на классическую программу на не тотальном языке с отдельным окончанием на Programместном языке .

Жиль "ТАК - перестань быть злым"
источник
Теперь я знаю, что это то, что я просил!
yhirai
будет ли какая-то разница, если я нажму fix pair1 rна вторую ветвь верхнего уровня match(и, конечно, адаптирую первую ветвь, чтобы она соответственно возвращала тип функции)?
день
@plmday: оба пути работают. Они экстенсионально эквивалентны для некоторого разумного определения экстенсиональности, и что более важно, они оба хорошо типизированы (перезапись экстенсиональности не меняет ни одного из соответствующих свойств ковариации (положительности)).
Жиль "ТАК - перестань быть злым"