idris 中列表的简单等式证明(证明 xs ++ [x] = ys ++ [y] -> x = y -> xs = ys)

Simple equality proofs on lists in idris (prooving xs ++ [x] = ys ++ [y] -> x = y -> xs = ys)

我正在学习 idris 并且对证明列表的属性非常感兴趣。

如果你看一下 standard library,可以证明 "Two lists are equal, if their heads are equal and their tails are equal."

consCong2 : {x : a} -> {xs : List a} -> {y : b} -> {ys : List b} ->
x = y -> xs = ys -> x :: xs = y :: ys
consCong2 Refl Refl = Refl

我们也可以证明另一个方向

consCong2' : {x : a} -> {xs : List a} -> {y : b} -> {ys : List b} ->
x::xs = y::ys -> x = y -> xs = ys
consCong2' Refl Refl = Refl

或者更明确地说,我们可以把方程两边的头都放下来得到证明。

consCong2' : {x : a} -> {xs : List a} -> {y : b} -> {ys : List b} ->
x::xs = y::ys -> x = y -> xs = ys
consCong2' prf_cons Refl = (cong {f = drop 1} prf_cons)

看看这些属性是否也可以证明其余的最后一项以及之前的所有内容,这将很有趣。 证明 "Two lists are equal, if the first part and the very last item are equal" 结果非常容易

consCong2' : {x : a} -> {xs : List a} -> {y : b} -> {ys : List b} ->
x = y -> xs = ys -> xs ++ [x] = ys ++ [y]
consCong2' Refl Refl = Refl

但是另一个方向没有通过类型检查:

consCong2' : {x : a} -> {xs : List a} -> {y : b} -> {ys : List b} ->
xs ++ [x] = ys ++ [y] -> x = y -> xs = ys
consCong2' Refl Refl = Refl

因为

Type mismatch between
                         ys ++ [x]
                 and
                         xs ++ [x]

显然,因为如果xs ++ [x] = ys ++ [y]在推导中排在第一位,idris不可能统一双方。

所以我们需要做的就是告诉 idris 在等式两边应用 init, 就像我们之前对 drop 1 所做的一样。 这失败了,因为 init 需要证明列表是非空的,这在此处不能以某种方式隐式推断。 所以为此我写了一个辅助函数,它明确定义了 (a ++ [x]) = a.

dropOneRight :  (xs : List a) -> List a
dropOneRight xs with (snocList xs)
  dropOneRight [] | Empty = []
  dropOneRight (a ++ [x]) | Snoc rec = a

consCong2' : {x : a} -> {xs : List a} -> {y : b} -> {ys : List b} ->
xs ++ [x] = ys ++ [y] -> x = y -> xs = ys
consCong2' prf_cons Refl = cong {f = dropOneRight} prf_cons

但这会产生

Type mismatch between
                 dropOneRight (ys ++ [x])
         and
                 ys

我花了一些时间在其他方法上,使用 snocList 进行案例分割,但没有取得显着进展。我不明白如何向 idris 展示 dropOneRight (ys ++ [x]) = ys。我如何证明 xs ++ [x] = ys ++ [y] -> x = y -> xs = ys

我想 snocList 方法会很棘手;至少使用遵循定义的简单证明策略。为了证明ys = dropOneRight (ys ++ [x])统一snocList (ys ++ [x])和参数会出问题,大致是:

prf' : (ys : List a) -> (x : a) -> ys = dropOneRight (ys ++ [x])
prf' ys x with (snocList (ys ++ [x]))
    ...
    prf' ?? x | Snoc rec = ?hole

如果你允许 dropOneRight 更简单,那就更直接了:

dropOneRight : (xs : List a) -> List a
dropOneRight [] = []
dropOneRight [x] = []
dropOneRight (x :: y :: xs) = x :: dropOneRight (y :: xs)

dropPrf : (ys : List a) -> (x : a) -> ys = dropOneRight (ys ++ [x])
dropPrf [] z = Refl
dropPrf [x] z = Refl
dropPrf (x :: y :: xs) z = cong $ dropPrf (y::xs) z

consCong2' : {x : a} -> {xs : List a} -> {y : b} -> {ys : List b} ->
             xs ++ [x] = ys ++ [y] -> x = y -> xs = ys
consCong2' {xs} {ys} {x} prf_cons Refl = 
  rewrite dropPrf ys x in rewrite dropPrf xs x in
  cong prf_cons