如何在HOLCF中证明列表的双重还原不会改变它

时间:2017-03-09 11:58:09

标签: isabelle

这是一个简单的理论,写在简单的HOL上:

theory ToyList
  imports Main
begin

no_notation Nil ("[]") and Cons (infixr "#" 65) and append (infixr "@" 65)
hide_type list
hide_const rev

datatype 'a list = Nil ("[]") | Cons 'a "'a list" (infixr "#" 65)

primrec snoc :: "'a list => 'a => 'a list" (infixr "#>" 65)
  where
    "[] #> y = y # []" |
    "(x # xs) #> y = x # (xs #> y)"

primrec rev :: "'a list => 'a list"
  where
    "rev [] = []" |
    "rev (x # xs) = (rev xs) #> x"

lemma rev_snoc [simp]: "rev(xs #> y) = y # (rev xs)"
  apply(induct_tac xs)
  apply(auto)
done

theorem rev_rev [simp]: "rev(rev xs) = xs"
  apply(induct_tac xs)
  apply(auto)
done

end

snoccons相反。它会在列表末尾添加一个项目。

我想通过HOLCF证明一个类似的引理。在第一阶段,我只考虑严格的清单。我在HOLCF中声明了严格列表的域名。我还声明了两个递归函数:

  • ssnoc - 将项目附加到列表的末尾
  • srev - 撤消列表

前缀s表示“严格”。

theory Test
  imports HOLCF
begin

domain 'a SList = SNil | SCons "'a" "'a SList"

fixrec ssnoc :: "'a SList → 'a → 'a SList"
  where
    "ssnoc ⋅ SNil ⋅ x = SCons ⋅ x ⋅ SNil" |
    "ssnoc ⋅ ⊥ ⋅ x = ⊥" |
    "x ≠ ⊥ ∧ xs ≠ ⊥ ⟹ ssnoc ⋅ (SCons ⋅ x ⋅ xs) ⋅ y = SCons ⋅ x ⋅ (ssnoc ⋅ xs ⋅ y)"

fixrec srev :: "'a SList → 'a SList"
  where
    "srev ⋅ ⊥ = ⊥" |
    "srev ⋅ SNil = SNil" |
    "x ≠ ⊥ ∧ xs ≠ ⊥ ⟹ srev ⋅ (SCons ⋅ x ⋅ xs) = ssnoc ⋅ (srev ⋅ xs) ⋅ x"

lemma srev_singleton [simp]:
  "srev ⋅ (SCons ⋅ a ⋅ SNil) = SCons ⋅ a ⋅ SNil"
  apply(induct)
  apply(simp_all)
done

lemma srev_ssnoc [simp]:
  "srev ⋅ (ssnoc ⋅ xs ⋅ a) = SCons ⋅ a ⋅ (srev ⋅ xs)"
  apply(induct xs)
  apply(simp_all)
done

lemma srev_srev [simp]:
  "srev ⋅ (srev ⋅ xs) = xs"
  apply(induct xs)
  apply(simp_all)
done

end

我试图证明列表的双重回复等于原始列表(srev_srev引理)。我已经宣布了两个辅助引理:

  • srev_singleton - 单例列表的反向是原始单例列表
  • srev_ssnoc - 列表的还原等于从原始列表的最后一项开始的列表,附加原始列表的其余项目的还原

但我无法证明任何一个引理。你能指出错误吗?

为什么在函数定义中需要前置条件"x ≠ ⊥ ∧ xs ≠ ⊥"?为什么我要明确声明"srev ⋅ ⊥ = ⊥""ssnoc ⋅ ⊥ ⋅ x = ⊥"。我想在HOLCF中,如果任何参数未定义,默认情况下函数是未定义的。

1 个答案:

答案 0 :(得分:2)

如果你的目的是建模列表la Haskell(又名"懒惰列表"),那么你应该使用类似的东西:

PHPUnit 3.7.21 by Sebastian Bergmann.

(注意"懒惰" domain 'a list = Nil ("[]") | Cons (lazy 'a) (lazy "'a list") (infix ":" 65) 的注释)。那么你不需要对你的第三个等式做出假设。如,

Cons

您所谓的fixrec append :: "'a list → 'a list → 'a list" where "append $ [] $ ys = ys" | "append $ (x : xs) $ ys = x : (append $ xs $ ys)"

ssnoc

反向。

然而,由于这种类型的列表允许"无限"价值观,你将无法证明fixrec reverse :: "'a list → 'a list" where "reverse $ [] = []" | "reverse $ (x : xs) = append $ xs $ (x : [])" 一般(因为它没有)。这仅适用于有限列表,可以归纳为特征。 (例如,请参阅https://arxiv.org/abs/1306.1340以获得更详细的讨论。)

但是,如果您不想为懒惰列表建模(即,实际上并不想要数据类型中的" lazy"注释),那么在没有假设的情况下,您的方程式可能无法实现。现在,如果方程具有这些假设,则它们仅适用于满足假设的情况。所以获得,你将无法证明reverse $ (reverse $ xs) = xs(没有额外的假设)。可能再次有可能通过归纳谓词获得适当的假设,但我没有进一步调查。

更新:在HOLCF中使用严格列表后,我还有更多评论:

首先,我的猜测是,由于内部构造,fixrec规范中的前提条件是必要的,但我们之后可以将它们除掉。

我设法证明你的引理如下。为了完整起见,我给出了理论文件的全部内容。首先要确保符号与现有符号没有冲突:

reverse $ (reverse $ xs) = xs

然后定义严格列表的类型

no_notation
  List.Nil ("[]") and
  Set.member ("op :") and
  Set.member ("(_/ : _)" [51, 51] 50)

和函数domain 'a list = Nil ("[]") | Cons 'a "'a list" (infixr ":" 65)

snoc

现在,我们通过以下方式获得第二个等式的无条件变体:

  1. 在第一个参数中显示fixrec snoc :: "'a list → 'a → 'a list" where "snoc $ [] $ y = y : []" | "x ≠ ⊥ ⟹ xs ≠ ⊥ ⟹ snoc $ (x:xs) $ y = x : snoc $ xs $ y" 是严格的(请注意snoc的用法)。
  2. 在第二个参数中显示fixrec_simp是严格的(此处需要归纳)。
  3. 最后,通过对所有三个变量的案例分析获得等式。
  4. snoc

    然后是函数lemma snoc_bot1 [simp]: "snoc $ ⊥ $ y = ⊥" by fixrec_simp lemma snoc_bot2 [simp]: "snoc $ xs $ ⊥ = ⊥" by (induct xs) simp_all lemma snoc_Cons [simp]: "snoc $ (x:xs) $ y = x : snoc $ xs $ y" by (cases "x = ⊥"; cases "xs = ⊥"; cases "y = ⊥";simp)

    reverse

    又是第二个等式的无条件变体:

    fixrec reverse :: "'a list → 'a list"
      where
        "reverse $ [] = []"
      | "x ≠ ⊥ ⟹ xs ≠ ⊥ ⟹ reverse $ (x : xs) = snoc $ (reverse $ xs) $ x"
    

    现在关于lemma reverse_bot [simp]: "reverse $ ⊥ = ⊥" by fixrec_simp lemma reverse_Cons [simp]: "reverse $ (x : xs) = snoc $ (reverse $ xs) $ x" by (cases "x = ⊥"; cases "xs = ⊥"; simp) reverse的引理你也有:

    snoc

    最后是理想的引理:

    lemma reverse_snoc [simp]: "reverse $ (snoc $ xs $ y) = y : reverse $ xs"
      by (induct xs) simp_all
    

    我获得此解决方案的方法只是查看失败尝试的剩余子目标,然后获得更多失败的尝试,查看剩余的子目标,重复,...