在Agda中,如何定义一对必须具有相同长度的向量?
-- my first try, but need to have 'n' implicitly
b : ∀ ( n : ℕ ) → Σ (Vec ℕ n) (λ _ → Vec ℕ n)
b 2 = (1 ∷ 2 ∷ []) , (3 ∷ 4 ∷ [])
b 3 = (1 ∷ 2 ∷ 3 ∷ []) , (4 ∷ 5 ∷ 6 ∷ [])
b _ = _
-- how can I define VecSameLength correctly?
VecSameLength : Set
VecSameLength = _
c : VecSameLength
c = (1 ∷ 2 ∷ []) , (1 ∷ 2 ∷ [])
d : VecSameLength
d = (1 ∷ 2 ∷ 3 ∷ []) , (4 ∷ 5 ∷ 6 ∷ [])
-- another try
VecSameLength : Set
VecSameLength = Σ (Vec ℕ ?) (λ v → Vec ℕ (len v))
答案 0 :(得分:9)
如果要将长度保持为类型的一部分,则只需要打包两个具有相同大小索引的向量。首先是必要的进口:
open import Data.Nat
open import Data.Product
open import Data.Vec
没有什么额外的幻想:就像你写出大小为n
的普通向量一样,你可以这样做:
2Vec : ∀ {a} → Set a → ℕ → Set a
2Vec A n = Vec A n × Vec A n
也就是说,2Vec A n
是A
s的矢量对的类型,两者都带有n
个元素。请注意,我借此机会将其概括为仲裁Universe级别 - 这意味着您可以使用Set
s的向量。
第二个有用的注意事项是我使用_×_
,这是一个普通的非依赖对。它以Σ
的形式定义为特殊情况,其中第二个组件不依赖于第一个组件的值。
在我转到我们想要隐藏大小的示例之前,这是一个这种类型值的示例:
test₁ : 2Vec ℕ 3
-- We can also infer the size index just from the term:
-- test₁ : 2Vec ℕ _
test₁ = 0 ∷ 1 ∷ 2 ∷ [] , 3 ∷ 4 ∷ 5 ∷ []
当您尝试将两个不均匀大小的向量填充到该对中时,您可以检查Agda是否正确地抱怨。
隐藏指数是完全适合依赖对的工作。作为首发,这是你如何隐藏一个向量的长度:
data SomeVec {a} (A : Set a) : Set a where
some : ∀ n → Vec A n → SomeVec A
someVec : SomeVec ℕ
someVec = some _ (0 ∷ 1 ∷ [])
大小索引保持在类型签名之外,因此我们只知道内部的向量具有某些未知大小(有效地为您提供列表)。当然,每次我们需要隐藏索引时编写新的数据类型都会很烦人,因此标准库为我们提供了Σ
。
someVec : Σ ℕ λ n → Vec ℕ n
-- If you have newer version of standard library, you can also write:
-- someVec : Σ[ n ∈ ℕ ] Vec ℕ n
-- Older version used unicode colon instead of ∈
someVec = _ , 0 ∷ 1 ∷ []
现在,我们可以轻松地将其应用于上面给出的类型2Vec
:
∃2Vec : ∀ {a} → Set a → Set a
∃2Vec A = Σ[ n ∈ ℕ ] 2Vec A n
test₂ : ∃2Vec ℕ
test₂ = _ , 0 ∷ 1 ∷ 2 ∷ [] , 3 ∷ 4 ∷ 5 ∷ []
在这里,我们将使用不同的导入列表来防止名称冲突:
open import Data.List
open import Data.Nat
open import Data.Product as P
open import Data.Vec as V
open import Function
open import Relation.Binary.PropositionalEquality
从两个向量到一个列表是将两个向量压缩在一起的问题:
vec⟶list : ∀ {a} {A : Set a} → ∃2Vec A → List (A × A)
vec⟶list (zero , [] , []) = []
vec⟶list (suc n , x ∷ xs , y ∷ ys) = (x , y) ∷ vec⟶list (n , xs , ys)
-- Alternatively:
vec⟶list = toList ∘ uncurry V.zip ∘ proj₂
回去只是解压缩 - 获取对列表并生成一对列表:
list⟶vec : ∀ {a} {A : Set a} → List (A × A) → ∃2Vec A
list⟶vec [] = 0 , [] , []
list⟶vec ((x , y) ∷ xys) with list⟶vec xys
... | n , xs , ys = suc n , x ∷ xs , y ∷ ys
-- Alternatively:
list⟶vec = ,_ ∘ unzip ∘ fromList
现在,我们知道如何从一个表示到另一个表示,但我们仍然必须证明这两个表示给我们相同的信息。
首先,我们展示如果我们获取一个列表,将其转换为vector(通过list⟶vec
)然后返回列表(通过vec⟶list
),然后我们将返回相同的列表。
pf₁ : ∀ {a} {A : Set a} (xs : List (A × A)) → vec⟶list (list⟶vec xs) ≡ xs
pf₁ [] = refl
pf₁ (x ∷ xs) = cong (_∷_ x) (pf₁ xs)
然后反过来:首先列出向量,然后列出向量:
pf₂ : ∀ {a} {A : Set a} (xs : ∃2Vec A) → list⟶vec (vec⟶list xs) ≡ xs
pf₂ (zero , [] , []) = refl
pf₂ (suc n , x ∷ xs , y ∷ ys) =
cong (P.map suc (P.map (_∷_ x) (_∷_ y))) (pf₂ (n , xs , ys))
如果您想知道cong
做了什么:
cong : ∀ {a b} {A : Set a} {B : Set b}
(f : A → B) {x y} → x ≡ y → f x ≡ f y
cong f refl = refl
我们已经证明list⟶vec
与vec⟶list
一起构成List (A × A)
和∃2Vec A
之间的同构,这意味着这两个表示形式是同构