Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
190 views
in Technique[技术] by (71.8m points)

agda - How to enumerate the elements of a list by `Fin`s in linear time?

We can enumerate the elements of a list like this:

-- enumerate-? = zip [0..]
enumerate-? : ? {α} {A : Set α} -> List A -> List (? × A)
enumerate-? = go 0 where
  go : ? {α} {A : Set α} -> ? -> List A -> List (? × A)
  go n  []      = []
  go n (x ∷ xs) = (n , x) ∷ go (?.suc n) xs

E.g. enumerate-? (1 ∷ 3 ∷ 2 ∷ 5 ∷ []) is equal to (0 , 1) ∷ (1 , 3) ∷ (2 , 2) ∷ (3 , 5) ∷ []. Assuming there is sharing in Agda, the function is linear.

However if we try to enumerate the elements of a list by Fins rather than ?s, the function becomes quadratic:

enumerate-Fin : ? {α} {A : Set α} -> (xs : List A) -> List (Fin (length xs) × A)
enumerate-Fin  []      = []
enumerate-Fin (x ∷ xs) = (zero , x) ∷ map (pmap suc id) (enumerate-Fin xs)

Is it possible to enumerate by Fins in linear time?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Consider this as a first attempt:

go : ? {m α} {A : Set α} -> Fin m -> (xs : List A) -> List (Fin (m + length xs) × A)
go i  []      = []
go i (x ∷ xs) = (inject+ _ i , x) ∷ {!go (suc i) xs!}

i grows at each recursive call as it should, but there is a mismatch:

the type of the goal is List (Fin (.m + suc (length xs)) × .A)

the type of the expression insise the hole is List (Fin (suc (.m + length xs)) × .A)

It's easy to prove that two types are equal, but it's also dirty. This is a common problem: one argument grows and the other lowers, so we need definitionally commutative _+_ to handle both the cases, but there is no way to define it. The solution is to use CPS:

go : ? {α} {A : Set α} -> (k : ? -> ?) -> (xs : List A) -> List (Fin (k (length xs)) × A)
go k  []      = []
go k (x ∷ xs) = ({!!} , x) ∷ go (k ° suc) xs

(k ° suc) (length xs) is the same thing as k (length (x ∷ xs)), hence the mismatch is fixed, but what is i now? The type of the hole is Fin (k (suc (length xs))) and it's uninhabited in the current context, so let's introduce some inhabitant:

go : ? {α} {A : Set α}
   -> (k : ? -> ?)
   -> (? {n} -> Fin (k (suc n)))
   -> (xs : List A)
   -> List (Fin (k (length xs)) × A)
go k i  []      = []
go k i (x ∷ xs) = (i , x) ∷ go (k ° suc) {!!} xs

The type of the new hole is {n : ?} → Fin (k (suc (suc n))). We can fill it with i, but i must grow at each recursive call. However suc and k doesn't commute, so suc i is Fin (suc (k (suc (_n_65 k i x xs)))). So we add another argument that sucs under k, and the final definition is

enumerate-Fin : ? {α} {A : Set α} -> (xs : List A) -> List (Fin (length xs) × A)
enumerate-Fin = go id suc zero where
  go : ? {α} {A : Set α}

     -> (k : ? -> ?)
     -> (? {n} -> Fin (k n) -> Fin (k (suc n)))
     -> (? {n} -> Fin (k (suc n)))
     -> (xs : List A)
     -> List (Fin (k (length xs)) × A)
  go k s i  []      = []
  go k s i (x ∷ xs) = (i , x) ∷ go (k ° suc) s (s i) xs

which works, because s : {n : ?} → Fin (k n) → Fin (k (suc n)) can be treated as {n : ?} → Fin (k (suc n)) → Fin (k (suc (suc n))).

A test: C-c C-n enumerate-Fin (1 ∷ 3 ∷ 2 ∷ 5 ∷ []) gives

(zero , 1) ∷
(suc zero , 3) ∷
(suc (suc zero) , 2) ∷ (suc (suc (suc zero)) , 5) ∷ []

Now note that in enumerate-Fin k always follows Fin in the types. Hence we can abstract Fin ° k and get a generic version of the function that works with both ? and Fin:

genumerate : ? {α β} {A : Set α}
           -> (B : ? -> Set β)
           -> (? {n} -> B n -> B (suc n))
           -> (? {n} -> B (suc n))
           -> (xs : List A)
           -> List (B (length xs) × A)
genumerate B s i  []      = []
genumerate B s i (x ∷ xs) = (i , x) ∷ genumerate (B ° suc) s (s i) xs

enumerate-? : ? {α} {A : Set α} -> List A -> List (? × A)
enumerate-? = genumerate _ suc 0

enumerate-Fin : ? {α} {A : Set α} -> (xs : List A) -> List (Fin (length xs) × A)
enumerate-Fin = genumerate Fin suc zero

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...