在Agda中分裂平等

时间:2014-02-01 22:35:06

标签: agda theorem-proving

有人如何证明这种平等

≡splitAt : {α : Level} {A : Set α} {l₁ l₂ : Nat}
         -> (xs₁ : Vec A l₁)
         -> (xs₂ : Vec A l₂)
         -> (xs₁ , xs₂ , refl) ≡ splitAt l₁ (xs₁ ++ xs₂)

?基本情况很明显

≡splitAt  []       xs₂ = refl

但是

≡splitAt (x ∷ xs₁) xs₂

给出

Goal: (x ∷ xs₁ , xs₂ , refl) ≡
      (splitAt (suc .n) (x ∷ xs₁ ++ xs₂)
       | splitAt .n (xs₁ ++ xs₂))

≡splitAt (x ∷ xs₁) xs₂ with ≡splitAt xs₁ xs₂
... | refl = {!!}

抛出一个错误:“拒绝解决异构约束反射...”。

这个

≡splitAt {l₁ = suc l₁} (x ∷ xs₁) xs₂ with splitAt l₁ (xs₁ ++ xs₂)
...                                  | (xs₁' , xs₂' , refl)

抛出一个错误:“xs 1!= xs 1'类型为Vec A l 1 ...”。 我写了这个定义:

++≡++ : {α : Level} {A : Set α} {l₁ l₂ : Nat}
      -> (xs₁ : Vec A l₁)
      -> (xs₂ : Vec A l₂)
      -> (xs₁' : Vec A l₁)
      -> (xs₂' : Vec A l₂)
      -> xs₁ ++ xs₂ ≡ xs₁' ++ xs₂'
++≡++ _ _ _ _ = {!!}

但不知道如何使用它。

也许有一些来源,我可以从中了解更多关于with-expressions的内容?

感谢。

1 个答案:

答案 0 :(得分:2)

在证明模式匹配定义的函数(例如此处为splitAt)时,一个好的经验法则是在证明中使用相同的模式。所以你在这里通过写

走在正确的轨道上
≡splitAt {l₁ = suc l₁} (x ∷ xs₁) xs₂ with splitAt l₁ (xs₁ ++ xs₂)
...                                  | (xs₁' , xs₂' , e) = ?

此处e的类型为xs₁ ++ xs₂ ≡ xs₁' ++ xs₂'。 Agda不知道如何解决这个等式,因为它包含函数_++_,所以你不能用refl替换它。所以我们必须改为帮助Agda:

split≡ : {α : Level} {A : Set α} (l₁ : Nat) {l₂ : Nat}
       -> (xs₁ xs₁' : Vec A l₁)
       -> (xs₂ xs₂' : Vec A l₂)
       -> xs₁ ++ xs₂ ≡ xs₁' ++ xs₂'
       -> (xs₁ ≡ xs₁') × (xs₂ ≡ xs₂')

zero的情况再次简单:

split≡ zero [] [] xs₂ .xs₂ refl = refl , refl

suc l₁的情况下,我们使用标准库中的cong将等式证明e分为头部相等和尾部相等,将最后一个引入递归调用分裂≡:

split≡ (suc l₁) (x ∷ xs₁) (x' ∷ xs₁') xs₂ xs₂' e with cong head e | split≡ l₁ xs₁ xs₁' xs₂ xs₂' (cong tail e)
split≡ (suc l₁) (x ∷ xs₁) (.x ∷ .xs₁) xs₂ .xs₂ e    | refl        | refl , refl = refl , refl 

现在我们有了split≡,我们可以回到≡splitAt:

的定义
≡splitAt {l₁ = suc l₁} (x ∷ xs₁) xs₂ | xs₁' , xs₂' , e with split≡ l₁ xs₁ xs₁' xs₂ xs₂' e
≡splitAt {l₁ = suc l₁} (x ∷ xs₁) xs₂ | .xs₁ , .xs₂ , e | refl , refl = {!!}

我们现在差不多了:我们知道xs₁ = xs₁'xs₂ = xs₂',但还不知道e = refl。不幸的是,e上的模式匹配不起作用:

xs₁ != xs₁' of type Vec A l₁
when checking that the pattern refl has type
xs₁ ++ xs₂ ≡ xs₁' ++ xs₂'

原因是Agda从左到右考虑模式,但我们想要一个不同的顺序。另一个带有模式的人来救援:

≡splitAt {l₁ = suc l₁} (x ∷ xs₁) xs₂ | xs₁' , xs₂' , e with split≡ l₁ xs₁ xs₁' xs₂ xs₂' e | e
≡splitAt {α} {A} {ℕ.suc l₁} (x ∷ xs₁) xs₂ | .xs₁ , .xs₂ , e | refl , refl | refl = refl

以下是我的完整代码供参考:

split≡ : {α : Level} {A : Set α} (l₁ : Nat) {l₂ : Nat}
       -> (xs₁ xs₁' : Vec A l₁)
       -> (xs₂ xs₂' : Vec A l₂)
       -> xs₁ ++ xs₂ ≡ xs₁' ++ xs₂'
       -> (xs₁ ≡ xs₁') × (xs₂ ≡ xs₂')
split≡ zero [] [] xs₂ .xs₂ refl = refl , refl
split≡ (suc l₁) (x ∷ xs₁) (x' ∷ xs₁') xs₂ xs₂' e with cong head e | split≡ l₁ xs₁ xs₁' xs₂ xs₂' (cong tail e)
split≡ (suc l₁) (x ∷ xs₁) (.x ∷ .xs₁) xs₂ .xs₂ e | refl | refl , refl = refl , refl

≡splitAt : {α : Level} {A : Set α} {l₁ l₂ : Nat}
         -> (xs₁ : Vec A l₁)
         -> (xs₂ : Vec A l₂)
         -> (xs₁ , xs₂ , refl) ≡ splitAt l₁ (xs₁ ++ xs₂)
≡splitAt [] xs₂ = refl
≡splitAt {l₁ = suc l₁} (x ∷ xs₁) xs₂ with splitAt l₁ (xs₁ ++ xs₂)
≡splitAt {l₁ = suc l₁} (x ∷ xs₁) xs₂ | xs₁' , xs₂' , e with split≡ l₁ xs₁ xs₁' xs₂ xs₂' e | e
≡splitAt {l₁ = suc l₁} (x ∷ xs₁) xs₂ | .xs₁ , .xs₂ , e | refl , refl | refl = refl

可能有一种更简单的方法来证明这一点,但这是我能想到的最好的方法。

关于如何更多地了解有关模式的问题,最好的学习方法是自己编写模式(至少我学习的方式)。不要忘记让Agda帮助您进行区分(通过在Emacs中使用C-c C-c)。