如何证明列表拆分有效?

时间:2014-01-06 19:20:53

标签: agda

我在锻炼身体。这似乎是一件微不足道的事情(简化以显示问题显然在列表拆分中):

  infixr 4 _::_ _++_ _==_

  data _==_ {A : Set} : (x : A) -> (y : A) -> Set where
    refl : (x : A) -> x == x

  data List (A : Set) : Set where
    nil  : List A
    _::_ : A -> List A -> List A

  _++_ : forall {A} -> List A -> List A -> List A
  nil ++ ys = ys
  (x :: xs) ++ ys = x :: (xs ++ ys)

  data Permutation {A : Set} : List A -> List A -> Set where
    pnil : Permutation nil nil
    p::  : forall {xs} -> (x : A) -> 
                          (zs : List A) -> (y : A) -> (ys : List A) ->
                          x == y -> Permutation xs (zs ++ ys) ->
                          Permutation (x :: xs) (zs ++ (y :: ys))

  lemma-ripTop : forall {A} -> (xs : List A) ->
                               (y : A) -> (ys : List A) ->
                               Permutation xs (y :: ys) ->
                               Permutation xs (y :: ys)
  lemma-ripTop nil y ys ()
  lemma-ripTop (x :: xs) y ys (p:: .x zs y1 ys1 x==y1 ps) = 
                          p:: x zs y1 ys1 x==y1 ps

长短不一,我声明可以定义两个列表的排列,如果它们可以提供Permutation个较小的列表,其中包含一对相等的元素x和{{1} } y的插入位置由yzs确定。

然后ys(意味着做一些完全不同的事情,但这里lemma-ripTop只是id)需要证明给出Permutation列表的内容( Permutation)。

  1. 我无法理解为什么Agda需要看y :: ys(这是我得到的错误) - 我认为这应该从类型声明和构造函数中清楚地看出来?即由于在输入时提供了zs ++ (y1 :: ys1) == y :: ys,因此在构造函数Permutation xs (y :: ys)中作为见证提供的拆分应该加起来为p::

  2. 如何说服Agda这个列表的拆分有效?

  3. 错误讯息:

    y :: ys

1 个答案:

答案 0 :(得分:1)

考虑以下片段(您可能知道这是身份类型/命题相等的归纳原则):

J : {A : Set} (P : (x y : A) → x == y → Set)
  (f : ∀ x → P x x (refl x)) (x y : A) (p : x == y) → P x y p
J P f x y p = ?

当我们在p上模式匹配并将其替换为refl时:

J P f x y (refl .x) = ?

阿格达会喊出那个

x != y of type A
when checking that the pattern refl .x has type x == y

这是因为xy不再是任意模式:p上的模式匹配揭示了这两者之间的关系。也就是说,xy必须是同一个东西。你必须写这个:

J P f x .x (refl .x) = f x

或许更好的例子是自然数的奇偶校验视图:

data Parity : ℕ → Set where
  even : ∀ k → Parity     (k * 2)
  odd  : ∀ k → Parity (1 + k * 2)

我们可以计算每个自然数的奇偶校验:

parity : ∀ n → Parity n
parity 0 = even 0
parity (suc n) with parity n
parity (suc     .(k * 2)) | even k = odd k
parity (suc .(1 + k * 2)) | odd  k = even (1 + k)

请注意,在模式匹配后,n不再是任意的 - 它必须是k * 2(如果是even)或1 + k * 2(如果是{{}} 1}})。


现在,让我们看看代码的问题在哪里。当您在置换参数上进行模式匹配时,oddy会变得固定 - 模式会显示其结构,就像上面的情况一样。所以你实际上已经重写了ysy来表达它们不再是任意的事实。但问题出在这里:如果你试着写下ysy的外观,你会发现你不能这样做。 ys可以是y的第一个元素,也可以是zs,具体取决于y1

zs

lemma-ripTop (x :: xs) .z .(zs ++ y1 :: ys1) (p:: .x (z :: zs) y1 ys1 x==y1 ps) = ? 上的模式匹配不幸无效。整个(虚线)模式应该是zs,但是在这里它被分成两个单独的模式,我想这是一个禁忌(不能说它是Agda不能做或不做的事,抱歉)


一种选择是将所有内容都移到校样中,我不确定它对你的情况有多大帮助,但是你走了。

首先,我们需要表达存在量化。依赖对(sigma类型)就是这样做的;这是标准库的定义:

z :: zs ++ y1 :: ys1

因此,例如,声明“存在一个自然数record Σ {a b} (A : Set a) (B : A → Set b) : Set (a ⊔ b) where constructor _,_ field proj₁ : A proj₂ : B proj₁ open Σ public n”将表示为n * n == 4,证明将是一对自然数和证明它的正方形确实是4.在我们的例子中,这将是Σ ℕ λ n → n * n == 4

我们可以将(2 , refl 4)拆分为l这一事实将通过zs ++ ysr₁的证据来表示,以便r₂和最终的排列是l == r₁ ++ r₂,然后我们需要证明k。这就是整个事情:

k == r₁ ++ y :: r₂

这可能会或可能不会令人愉快,但它始终忠实于您的原始实现。如果您愿意使用其他内容:您可以使用辅助数据类型表示data Permutation {A : Set} : (xs ys : List A) → Set a where nil : Permutation nil nil cons : (x y : A) (xs ys zs : List A) (p₁ : Σ _ λ r₁ → Σ _ λ r₂ → ys == r₁ ++ r₂) (p₂ : x == y) (p₃ : let (r₁ , r₂ , _) = p₁ in zs == r₁ ++ y :: r₂) → Permutation xs ys → Permutation (x :: xs) zs 位于x的某个位置:

xs

然后是排列:

data insert_into_==_ {A : Set} (x : A) : List A → List A → Set where
  here  : ∀ {xs}           → insert x into xs == (x :: xs)
  there : ∀ {y} {xs} {xys} → insert x into xs == xys →
                             insert x into (y :: xs) == (y :: xys)