有人如何证明这种平等
≡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的内容?
感谢。
答案 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)。