-
Notifications
You must be signed in to change notification settings - Fork 271
Add Queue datatype #3084
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add Queue datatype #3084
Changes from all commits
4905cc6
4125b12
bf7790b
d26d47c
6a8bf86
9056b95
85e54c5
95be562
bebe5a2
9ef66fb
a0b0353
253edac
100d5db
e3f8872
48b6afd
86bed0a
0bf49e7
0561f10
1c1d2c6
55792de
7e7edab
9810428
bedd0df
79b5d6c
5b36b00
7de85c2
1c56ada
5cacded
a3c6eda
c3b93fb
3c61400
a2d5854
4ae85fc
257a08b
612c743
e0b83dd
a071100
379375a
d724dfb
41e76dd
c2246b4
e8ff3b8
e2cc2c8
e790193
6a717da
d1a9ea9
2ec3eaa
99fc8f1
6fe776f
0512a11
009697c
b4e1ec7
07212a4
84d517b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| ------------------------------------------------------------------------ | ||
| -- The Agda standard library | ||
| -- | ||
| -- Queues | ||
| ------------------------------------------------------------------------ | ||
|
|
||
| {-# OPTIONS --without-K --safe #-} | ||
|
|
||
| module Data.Queue where | ||
|
|
||
| open import Data.Queue.TwoList.Base public | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,84 @@ | ||
| ------------------------------------------------------------------------ | ||
| -- The Agda standard library | ||
| -- | ||
| -- Queue specification | ||
| ------------------------------------------------------------------------ | ||
|
|
||
| {-# OPTIONS --without-K --safe #-} | ||
|
|
||
| module Data.Queue.QueueSpec where | ||
|
|
||
| open import Data.Bool.Base using (Bool) | ||
| open import Data.List.Base as List using (List; []; length; _∷_) | ||
| open import Data.List.Relation.Unary.All using (Null) | ||
| open import Data.Maybe.Base using (Maybe; nothing; just) | ||
| open import Data.Nat.Base using (ℕ) | ||
| open import Data.Product.Base using (_×_; proj₁; proj₂) | ||
| open import Function.Base using (_∘_) | ||
| open import Level | ||
| open import Relation.Binary.Core using (Rel; _=[_]⇒_) | ||
| open import Relation.Binary.Definitions using (_Respects_) | ||
| open import Relation.Binary.Structures using (IsEquivalence) | ||
| open import Relation.Binary.PropositionalEquality.Core using (_≡_) | ||
| open import Relation.Nullary.Decidable.Core using (yes; no; isYes; False; does) | ||
| open import Relation.Unary using (Pred; Decidable) | ||
|
|
||
| private | ||
| variable | ||
| a b : Level | ||
| A : Set a | ||
| B : Set b | ||
|
|
||
| -- RawQueue defines the 'computations' available on Queues | ||
| -- without any of the associated proofs that determine its | ||
| -- correctness. | ||
| record RawQueue (Q : Set a → Set a) : Set (suc a) where | ||
|
|
||
| field | ||
| _≈_ : ∀ {A : Set a} → Rel (Q A) a | ||
| Empty : ∀ {A : Set a} → Pred (Q A) a | ||
| empty? : Decidable (Empty {A = A}) | ||
| fromList : List A → Q A | ||
| toList : Q A → List A | ||
| enqueue : A → Q A → Q A | ||
| dequeue : (q : Q A) → .{{False (empty? q)}} → A × Q A | ||
|
|
||
| empty : Q A | ||
| empty = fromList [] | ||
|
|
||
| pure : A → Q A | ||
| pure = fromList ∘ List.[_] | ||
|
|
||
| size : Q A → ℕ | ||
| size = length ∘ toList | ||
|
|
||
| to𝔹 : Q A → Bool | ||
| to𝔹 = isYes ∘ empty? | ||
|
Comment on lines
+55
to
+56
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Similarly, should this be called |
||
|
|
||
| dequeue′ : Q A → Maybe (A × Q A) | ||
| dequeue′ q with empty? q in eq | ||
| ... | yes _ = nothing | ||
| ... | no _ = just (dequeue q) | ||
| where instance | ||
| _ : False (empty? q) | ||
| _ rewrite eq = _ | ||
|
|
||
| -- IsQueue bundles RawQueue with proofs of a Queues correctness, | ||
| -- such as enqueue adding 1 to the Queue's size | ||
| -- NOTE: not finished adding everything! | ||
| record IsQueue {Q : Set a → Set a} (rawQ : RawQueue Q) : Set (suc a) where | ||
|
|
||
| open RawQueue rawQ | ||
|
|
||
| field | ||
| isEquivalence : IsEquivalence (_≈_ {A = A}) | ||
| ≈-resp-Empty : Empty Respects (_≈_ {A = A}) | ||
| ≈-=[toList]⇒-≡ : (_≈_ {A = A}) =[ toList ]⇒ _≡_ | ||
| empty-toList : ∀ {q : Q A} → Empty q → Null (toList q) | ||
| empty-fromList : ∀ {xs : List A} → Null {A = A} xs → Empty (fromList xs) | ||
| toList-fromList : ∀ {q : Q A} {xs : List A} → q ≈ fromList xs → toList q ≡ xs | ||
| fromList-toList : ∀ {q : Q A} {xs : List A} → xs ≡ toList q → fromList xs ≈ q | ||
| toList-enqueue : ∀ {q : Q A} {x : A} → toList (enqueue x q) ≡ toList q List.∷ʳ x | ||
| -- for some reason, let x , r = ... doesn't bind x and r?? | ||
| toList-dequeue : ∀ {q : Q A} → .{{i : False (empty? q)}} → | ||
| let xr = dequeue q {{i}} in toList q ≡ proj₁ xr ∷ toList (proj₂ xr) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,155 @@ | ||
| ------------------------------------------------------------------------ | ||
| -- The Agda standard library | ||
| -- | ||
| -- Queues, basic types and operations | ||
| ------------------------------------------------------------------------ | ||
| {-# OPTIONS --without-K --safe #-} | ||
|
|
||
| -- Queues implemented with the two-list method described in | ||
| -- "Purely Functional Data Structures", Chris Okasaki, 1996 | ||
| -- | ||
| -- Note that the weaker invariant is used here that only guarantees | ||
| -- amortized O(1) when the structure is used non-persistently. | ||
|
|
||
| module Data.Queue.TwoList.Base where | ||
|
|
||
| open import Level using (Level) | ||
| open import Data.Bool.Base using (Bool; true; false) | ||
| open import Data.Empty using (⊥-elim) | ||
| open import Data.List.Base as List using (List; []; _∷_; reverse; _++_; length; null) | ||
| open import Data.List.Relation.Unary.All using (Null; []; _∷_) | ||
| open import Data.List.Relation.Unary.All.Properties using (null⇒Null; Null⇒null) | ||
| open import Data.Maybe.Base using (Maybe; nothing; just) | ||
| open import Data.Nat.Base using (ℕ; zero; suc; _+_) | ||
| open import Data.Product using (_×_; _,_; proj₂) | ||
| open import Data.Queue.QueueSpec using (RawQueue; IsQueue) | ||
| open import Data.SnocList.Base as SnocList using (List<; toList>; fromList>; []; _<:_) | ||
| open import Data.SnocList.Relation.Unary.All | ||
| open import Data.Unit.Base using (⊤) | ||
| open import Function.Base using (id; const; _∘_) | ||
| open import Relation.Binary.PropositionalEquality.Core using (_≡_) | ||
| open import Relation.Binary.Core using (Rel) | ||
| open import Relation.Nullary using (¬_) | ||
| open import Relation.Nullary.Decidable.Core using (yes; no; isYes; False) | ||
| open import Relation.Nullary.Reflects using (ofʸ; ofⁿ) | ||
| open import Relation.Unary using (Pred; Decidable) | ||
|
|
||
| private | ||
| variable | ||
| a b : Level | ||
| A : Set a | ||
| B : Set b | ||
|
|
||
| ¬null : {a : A} {as : List A} → ¬ (Null (a ∷ as)) | ||
| ¬null (() Data.List.Relation.Unary.All.∷ n) | ||
|
|
||
| ¬null< : {a : A} {as : List< A} → ¬ (Null< (as <: a)) | ||
| ¬null< (() Data.SnocList.Relation.Unary.All.<: n) | ||
|
|
||
| null-[] : ∀ {xs : List A} → Null xs → Null {A = A} [] | ||
| null-[] = const [] | ||
|
|
||
| null<-[] : ∀ {xs : List< A} → Null< xs → Null {A = A} [] | ||
| null<-[] = const [] | ||
|
|
||
| null-∷ : ∀ {x} {xs ys : List A} → Null (x ∷ xs) → Null ys | ||
| null-∷ (()∷ _) | ||
|
|
||
| null-<: : ∀ {x} {xs : List< A} {ys : List A} → Null< (xs <: x) → Null ys | ||
| null-<: (()<: _) | ||
|
|
||
| -- A Queue consists of a front (dequeue) and back (enqueue) list | ||
| -- When enqueing (unless it is the first element), elements are cons'd | ||
| -- to the enqueue list. | ||
| -- | ||
| -- When dequeuing, elements are taken from the head of the dequeue | ||
| -- list. If this is empty, the enqueue list is reversed and swapped | ||
| -- with the dequeue list. | ||
| -- | ||
| -- The dequeue-list should be empty iff the whole queue is empty. | ||
|
|
||
| record Queue (A : Set a) : Set a where | ||
| constructor mkQ | ||
| field | ||
| front : List< A | ||
| back : List A | ||
|
silas-hw marked this conversation as resolved.
|
||
| inv : Null< front → Null back | ||
|
|
||
| ------------------------------------------------------------------------ | ||
| --- Basic Functions/Relations/Operators | ||
|
|
||
| Empty : ∀ {A : Set a} → Pred (Queue A) a | ||
| Empty {a} {A} q = Null< (Queue.front q) | ||
|
|
||
| empty? : Decidable (Empty {A = A}) | ||
| empty? (mkQ front back inv) .Relation.Nullary.does = SnocList.null front | ||
| empty? (mkQ [] back inv) .Relation.Nullary.proof = ofʸ [] | ||
| empty? (mkQ (xs <: x) back inv) .Relation.Nullary.proof = ofⁿ (λ e → ⊥-elim (¬null< e)) | ||
|
|
||
| isEmpty : Queue A → Bool | ||
| isEmpty q = SnocList.null (Queue.front q) | ||
|
|
||
| ------------------------------------------------------------------------ | ||
| --- Smart Constructor | ||
|
|
||
| queue : List< A → List A → Queue A | ||
| queue [] ys = mkQ (fromList> (reverse ys)) [] null<-[] | ||
| queue xs@(_ <: _) ys = mkQ xs ys null-<: | ||
|
|
||
| ------------------------------------------------------------------------ | ||
| --- Conversion to/from List | ||
|
|
||
| -- Create a List from a Queue, such that the last that would be dequeued | ||
| -- becomes the head of the list | ||
| toList : Queue A → List A | ||
| toList q = (Queue.back q) ++ (toList> (Queue.front q)) | ||
|
|
||
| -- Create a Queue from a List, such that the elements | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably consider |
||
| -- of the list would be dequeued starting from its last element | ||
| fromList : List A → Queue A | ||
| fromList xs = queue (fromList> xs) [] | ||
|
|
||
| ------------------------------------------------------------------------ | ||
| -- Construction & Destruction | ||
|
|
||
| empty : Queue A | ||
| empty = fromList [] | ||
|
|
||
| enqueue : A → Queue A → Queue A | ||
| enqueue x q with bs ← Queue.back q | Queue.front q | ||
| ... | [] = queue ([] <: x) [] | ||
| ... | front@(_ <: _) = queue front (x ∷ bs) | ||
|
|
||
| dequeue : ∀ (q : Queue A) .{{_ : False (empty? q)}} → A × Queue A | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this will be ergonomic to use as opposed to the Note that the I'm not against having a
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm torn between the two directions myself; I don't have enough 'proof intuition' to know which is best! It's an easy change between the two for the most part, so I'll let other maintainers discuss :-)
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, in a way, the definition of I'd understood from @Taneb 's comments above that this was an opportunity for something other than the pure-haskell-ish style, and of course that style is a major influence on the 'core' of What name do you think the 'guarded' version should have?
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, and regarding the ergonomics (I've always been a bit shaky on dequeue : Q A → Maybe (A × Q A)
dequeue q with isEmpty q in eq
... | true = nothing
... | false = just (dequeue-guarded q)
where instance
_ : T (not (isEmpty q))
_ rewrite eq = _for the 'obvious' renamings of everything that has gone before, and unfolding |
||
| dequeue (mkQ (xs <: x) back _) = x , queue xs back | ||
|
|
||
| -- Create a queue with a single element | ||
| singleton : A → Queue A | ||
| singleton = fromList ∘ List.[_] | ||
|
|
||
| -- map : (A → B) → Queue A → Queue B | ||
| -- map f empty = empty | ||
| -- map f (queue x xs ys) = queue (f x) (List.map f xs) (List.map f ys) | ||
|
|
||
| ------------------------------------------------------------------------ | ||
| -- Relations | ||
|
|
||
| -- Under the property that toList returns a list in | ||
| -- the order of dequeue | ||
| _≈_ : ∀ {A : Set a} → Rel (Queue A) a | ||
| q ≈ q' = (toList q) ≡ (toList q') | ||
|
jamesmckinna marked this conversation as resolved.
|
||
|
|
||
| ------------------------------------------------------------------------ | ||
| --- TwoList Queue is a Queue | ||
|
|
||
| instance | ||
| TwoList-RawQueue : RawQueue {a} Queue | ||
| TwoList-RawQueue = record | ||
| { _≈_ = _≈_ | ||
| ; Empty = Empty | ||
| ; empty? = empty? | ||
| ; fromList = fromList | ||
| ; toList = toList | ||
| ; enqueue = enqueue | ||
| ; dequeue = dequeue | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this should itself be
Data.Queue.TwoListas the top-level (sic) module for this implementation?Again, up for discussion!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As in have a file
Data/Queue/TwoList.agdawith this content?Indeed, this file itself will be changed to not just have
TwoList.Baseimported, and will then likely importData,Queue.TwoListinstead?