这是Isabelle's Code generation: Abstraction lemmas for containers?的后续行动:
我想在以下理论中为the_question
生成代码:
theory Scratch imports Main begin
typedef small = "{x::nat. x < 10}" morphisms to_nat small
by (rule exI[where x = 0], simp)
code_datatype small
lemma [code abstype]: "small (to_nat x) = x" by (rule to_nat_inverse)
definition a_pred :: "small ⇒ bool"
where "a_pred = undefined"
definition "smaller j = [small i . i <- [0 ..< to_nat j]]"
definition "the_question j = (∀i ∈ set (smaller j). a_pred j)"
问题在于smaller
的等式不适合代码生成,因为它提到了抽象函数small
。
现在根据Andreas对我的上一个问题和关于数据优化的论文的回答,下一步是为小数字集引入一个类型,并为该类型中的smaller
创建一个定义:
typedef small_list = "{l. ∀x∈ set l. (x::nat) < 10}" by (rule exI[where x = "[]"], auto)
code_datatype Abs_small_list
lemma [code abstype]: "Abs_small_list (Rep_small_list x) = x" by (rule Rep_small_list_inverse)
definition "smaller' j = Abs_small_list [ i . i <- [0 ..< to_nat j]]"
lemma smaller'_code[code abstract]: "Rep_small_list (smaller' j) = [ i . i <- [0 ..< to_nat j]]"
unfolding smaller'_def
by (rule Abs_small_list_inverse, cases j, auto elim: less_trans simp add: small_inverse)
现在smaller'
是可执行的。根据我的理解,我需要重新定义small list
上的操作作为small_list
上的操作:
definition "small_list_all P l = list_all P (map small (Rep_small_list l))"
lemma[code]: "the_question j = small_list_all a_pred (smaller' j)"
unfolding small_list_all_def the_question_def smaller'_code smaller_def Ball_set by simp
我可以为the_question
定义好看的代码方程式。但是small_list_all
的定义不适合代码生成,因为它提到了抽象态射small
。如何使small_list_all
可执行文件?
(注意我无法解开a_pred
的代码方程,因为问题实际上出现在实际递归a_pred
的代码方程中。另外,我想避免涉及re的hack - 在运行时检查不变量。)
答案 0 :(得分:2)
我对一般问题没有很好的解决方案,但是这个想法会让你在这种特殊情况下为the_question
生成代码。
首先,使用抽象代码方程式定义函数predecessor :: "small ⇒ small
(可能使用lift_definition
中的λn::nat. n - 1
。
现在你可以为smaller
证明一个新的代码等式,其rhs使用if-then-else,predecessor
和普通列表操作:
lemma smaller_code [code]:
"smaller j = (if to_nat j = 0 then []
else let k = predecessor j in smaller k @ [k])"
(如果您愿意定义辅助功能,当然可以实现更有效的实现。)
代码生成现在应该适用于smaller
,因为此代码方程式不使用函数small
。
答案 1 :(得分:0)
简短回答是no, it does not work。
答案很长,经常有变通方法。布莱恩在答案中展示了一个。一般的想法似乎是
将除了最终返回值之外的协变位置中具有抽象类型的函数(即返回抽象值的容器的高阶函数或函数)分离为多个辅助函数,以便抽象值仅构造为单个返回值1帮助函数。
在Brian的例子中,这个函数是predecessor
。或者,作为另一个简单的例子,假设一个函数
definition smallPrime :: "nat ⇒ small option"
where "smallPrime n = (if n ∈ {2,3,5,7} then Some (small n) else None)"
由于small
的出现,此定义不是有效的代码方程式。但这得出一个:
definition smallPrimeHelper :: "nat ⇒ small"
where "smallPrimeHelper n = (if n ∈ {2,3,5,7} then small n else small 0)"
lemma [code abstract]: "to_nat (smallPrimeHelper n) = (if n ∈ {2,3,5,7} then n else 0)"
by (auto simp add: smallPrimeHelper_def intro: small_inverse)
lemma [code_unfold]: "smallPrime n = (if n ∈ {2,3,5,7} then Some (smallPrimeHelper n) else None)"
unfolding smallPrime_def smallPrimeHelper_def by simp
如果想要避免谓词的冗余计算(可能比∈ {2,3,5,7}
更复杂),可以通过引入抽象视图使得帮助器的返回类型更智能,即包含两者的类型计算的结果,以及从中构造抽象类型所需的信息:
typedef smallPrime_view = "{(x::nat, b::bool). x < 10 ∧ b = (x ∈ {2,3,5,7})}"
by (rule exI[where x = "(2, True)"], auto)
setup_lifting type_definition_small
setup_lifting type_definition_smallPrime_view
对于视图,我们有一个构建它的函数和访问器,它们将结果分开,有一些关于它们的引理:
lift_definition smallPrimeHelper' :: "nat ⇒ smallPrime_view"
is "λ n. if n ∈ {2,3,5,7} then (n, True) else (0, False)" by simp
lift_definition smallPrimeView_pred :: "smallPrime_view ⇒ bool"
is "λ spv :: (nat × bool) . snd spv" by auto
lift_definition smallPrimeView_small :: "smallPrime_view ⇒ small"
is "λ spv :: (nat × bool) . fst spv" by auto
lemma [simp]: "smallPrimeView_pred (smallPrimeHelper' n) ⟷ (n ∈ {2,3,5,7})"
by transfer simp
lemma [simp]: "n ∈ {2,3,5,7} ⟹ to_nat (smallPrimeView_small (smallPrimeHelper' n)) = n"
by transfer auto
lemma [simp]: "n ∈ {2,3,5,7} ⟹ smallPrimeView_small (smallPrimeHelper' n) = small n"
by (auto intro: iffD1[OF to_nat_inject] simp add: small_inverse)
有了这个,我们可以得到一个只进行一次检查的代码方程式:
lemma [code]: "smallPrime n =
(let spv = smallPrimeHelper' n in
(if smallPrimeView_pred spv
then Some (smallPrimeView_small spv)
else None))"
by (auto simp add: smallPrime_def Let_def)