【发布时间】:2014-03-01 03:44:14
【问题描述】:
假设我们有一个排序列表的数据类型,带有与证明无关的排序见证。我们将使用 Agda 的实验性大小类型功能,这样我们就有希望在数据类型上获得一些递归函数来通过 Agda 的终止检查器。
{-# OPTIONS --sized-types #-}
open import Relation.Binary
open import Relation.Binary.PropositionalEquality as P
module ListMerge
{???? ℓ}
(A : Set ????)
{_<_ : Rel A ℓ}
(isStrictTotalOrder : IsStrictTotalOrder _≡_ _<_) where
open import Level
open import Size
data SortedList (l u : A) : {ι : _} → Set (???? ⊔ ℓ) where
[] : {ι : _} → .(l < u) → SortedList l u {↑ ι}
_∷[_]_ : {ι : _} (x : A) → .(l < x) → (xs : SortedList x u {ι}) →
SortedList l u {↑ ι}
现在,我们想在这样的数据结构上定义一个经典函数merge,它接受两个排序列表,并输出一个排序列表,其中包含输入列表的元素。
open IsStrictTotalOrder isStrictTotalOrder
merge : ∀ {l u} → SortedList l u → SortedList l u → SortedList l u
merge xs ([] _) = xs
merge ([] _) ys = ys
merge (x ∷[ l<x ] xs) (y ∷[ l<y ] ys) with compare x y
... | tri< _ _ _ = x ∷[ l<x ] (merge xs (y ∷[ _ ] ys))
merge (x ∷[ l<x ] xs) (.x ∷[ _ ] ys) | tri≈ _ P.refl _ =
x ∷[ l<x ] (merge xs ys)
... | tri> _ _ _ = y ∷[ l<y ] (merge (x ∷[ _ ] xs) ys)
这个函数看起来很无害,但要让 Agda 相信它是完全的可能很棘手。实际上,如果没有任何明确的大小索引,该函数就无法进行终止检查。一种选择是split the function into two mutually recursive definitions。这行得通,但给定义和相关证明增加了一定量的冗余。
但同样,我不确定是否甚至可以明确给出大小索引,以便merge 具有 Agda 将接受的签名。 These slides 明确讨论 mergesort;那里给出的签名表明以下应该有效:
merge′ : ∀ {l u} → {ι : _} → SortedList l u {ι} →
{ι′ : _} → SortedList l u {ι′} → SortedList l u
merge′ xs ([] _) = xs
merge′ ([] _) ys = ys
merge′ (x ∷[ l<x ] xs) (y ∷[ l<y ] ys) with compare x y
... | tri< _ _ _ = x ∷[ l<x ] (merge′ xs (y ∷[ _ ] ys))
merge′ (x ∷[ l<x ] xs) (.x ∷[ _ ] ys) | tri≈ _ P.refl _ =
x ∷[ l<x ] (merge′ xs ys)
... | tri> _ _ _ = y ∷[ l<y ] (merge' (x ∷[ _ ] xs) ys)
我们在这里所做的是允许输入具有任意(和不同)大小,并指定输出的大小为 ∞。
不幸的是,使用此签名,Agda 在检查定义第一条的正文 xs 时抱怨 .ι != ∞ of type Size。我并没有声称非常了解大小类型,但我的印象是任何大小 ι 都会与 ∞ 统一。自从编写这些幻灯片以来,大小类型的语义可能已经发生了变化。
那么,我的场景是一个使用大小类型的用例吗?如果是这样,我应该如何使用它们?如果大小类型在这里不合适,为什么上面的merge 的第一个版本不是终止检查,因为the following does:
open import Data.Nat
open import Data.List
open import Relation.Nullary
merge : List ℕ → List ℕ → List ℕ
merge (x ∷ xs) (y ∷ ys) with x ≤? y
... | yes p = x ∷ merge xs (y ∷ ys)
... | _ = y ∷ merge (x ∷ xs) ys
merge xs ys = xs ++ ys
【问题讨论】:
-
首先我不知道是否与问题有关,我只是了解Agda的存在。我只想提一下,如果在 Coq 中终止合并需要一个特定的技巧是否有帮助,例如在 adam.chlipala.net/cpdt/html/GeneralRec.html 中进行了描述
-
如果你重建
xs和ys更大的尺寸,你可以让你的merge′工作,比如promote : ∀ {l u i} → SortedList l u {i} → SortedList l u {∞}。 -
我确实想知道这一点(虽然它看起来有点疯狂,但仍然如此:)。我想有人会确定promote本质上是
id,这可能会使对证明的影响保持可控。谢谢,有机会我会看看这个。 -
我有机会使用
FiniteMap类型和unionWith函数I asked about before 来试验您的建议。它似乎工作得很好,所以我根据大小类型为我之前的问题添加了一个新答案。至于这个问题,我建议您发表评论以回答;这可能会让它更显眼。
标签: merge sortedlist termination agda