Zulip Chat Archive

Stream: Is there code for X?

Topic: enumerate nonempty lists


Rob Lewis (Jan 07 2022 at 05:35):

Is there a function like def flatten {ι : Type*} (f : ℕ → {l : list ι // l ≠ []}) : ℕ → ι or similar? Given a sequence of nonempty lists, it should enumerate the elements of the lists in order.

Yakov Pechersky (Jan 07 2022 at 05:50):

Where n is the n-th element of the lists, when they've all be concatenated and sorted?

Rob Lewis (Jan 07 2022 at 05:57):

Sorry, "in order" is ambiguous there. I mean "in the order they appear." So if f 0 = [a, b], f 1 = [c], f 2 = [a, d, c], ..., I want the outpu to be a, b, c, a, d, c, ...

Rob Lewis (Jan 07 2022 at 05:57):

No sorting needed.

Rob Lewis (Jan 07 2022 at 05:58):

The nth element of the infinite concatenation

Yaël Dillies (Jan 07 2022 at 06:21):

I feel like you shoudn't require the lists to be nonempty. Then what you're asking for is roughly docs#list.join composed with docs#list.nth, but there's still a gap.

Patrick Johnson (Jan 07 2022 at 06:31):

docs#list.join is for (finite) lists of lists, but the question is about nat -> list, which is infinite. Also, if lists are allowed to be empty, then for some number the result may be undefined.

I wrote this definition (untested):

import tactic
open nat
open list

def f₁ {α : Type*} (f :   {l : list α // l  []}) (i : ) : {l : list α // l  []} :=
dite ((f 0).1.length = 1) (λ h, f i.succ)
(λ h, ite (i = 0) tail (f 0).1, begin
  have h₁ := (f 0).2,
  cases (f 0).val with x xs,
  { exact (h₁ rfl).elim },
  { dsimp at h, rw [add_left_eq_self, length_eq_zero] at h, exact h },
end (f i))

def f₂ {α : Type*} (xs : {l : list α // l  []}) : inhabited α :=
by { cases xs with xs h, cases xs with x xs, exact (h rfl).elim, use x }

def flatten {α : Type*} : (  {l : list α // l  []})    α
| f 0 := @head α (f₂ (f 0)) (f 0).1
| f (succ n) := flatten (f₁ f) n

Rob Lewis (Jan 07 2022 at 06:31):

If the lists aren't nonempty, such a function may not exist: I can define f : ℕ → list empty but not f : ℕ → empty.

Rob Lewis (Jan 07 2022 at 06:34):

Thanks, Patrick, that's clever -- gotta process that one a bit.

Yaël Dillies (Jan 07 2022 at 06:37):

Patrick Johnson said:

docs#list.join is for (finite) lists of lists, but the question is about nat -> list, which is infinite. Also, if lists are allowed to be empty, then for some number the result may be undefined.

I am aware of that :smile:

Rob Lewis (Jan 07 2022 at 06:53):

Bedtime for me, but a challenge if you're in the mood to test your definition :smile:

import tactic
open nat
open list

def f₁ {α : Type*} (f :   {l : list α // l  []}) (i : ) : {l : list α // l  []} :=
dite ((f 0).1.length = 1) (λ h, f i.succ)
(λ h, ite (i = 0) tail (f 0).1, begin
  have h₁ := (f 0).2,
  cases (f 0).val with x xs,
  { exact (h₁ rfl).elim },
  { dsimp at h, rw [add_left_eq_self, length_eq_zero] at h, exact h },
end (f i))

def f₂ {α : Type*} (xs : {l : list α // l  []}) : inhabited α :=
by { cases xs with xs h, cases xs with x xs, exact (h rfl).elim, use x }

def flatten' {α : Type*} : (  {l : list α // l  []})    α
| f 0 := @head α (f₂ (f 0)) (f 0).1
| f (succ n) := flatten' (f₁ f) n

def flatten {α : Type*} (f :   list α) (hf :  n, f n  []) :   α :=
flatten' (λ k, f k, hf k⟩)

-- `f` collects elements of `α` that have the same value under the map `v`,
-- and collects them ordered by this value
variables {α β : Type*} [linear_order β] {v : α  β}
  (f :   list α)
  (hf :  n, f n  [])
  (hv1 :  n,  i j  f n, v i = v j)
  (hv2 :  n,  i  f n,  j  f (n+1), v i < v j)
include hv1 hv2

lemma flatten_le (n : ) : v (flatten f hf n)  v (flatten f hf (n+1)) :=
sorry

Yaël Dillies (Jan 07 2022 at 06:55):

Note that you could also define the analogous thing for Z\Z, so maybe this should have a N\N-specific name.

Patrick Johnson (Jan 07 2022 at 08:45):

@Rob Lewis Thank you for the nice challenge! Here you go:

lemma flatten_le (n : ) : v (flatten f hf n)  v (flatten f hf (n+1)) :=
begin
  revert f, induction n with n hn; rintro f hf hv1 hv2,
  { rw [flatten, flatten', flatten', flatten', f₁], dsimp, split_ifs,
    { dsimp, apply le_of_lt, apply hv2;
      { apply head_mem_self, apply hf }},
    { apply le_of_eq, dsimp, apply hv1,
      { apply head_mem_self, apply hf },
      { apply mem_of_mem_tail, apply head_mem_self,
        have h₁ := hf 0, cases f 0 with x xs,
        { apply (h₁ rfl).elim },
        { rw tail, simp at h, rw length_eq_zero at h, exact h }}}},
  { generalize hf' : (λ n, (f₁ (λ n, f n, hf n⟩) n).1) = f',
    have hf' :  n, f' n  [],
    { intro k, subst f', dsimp, rw f₁,
      split_ifs with h h₁; dsimp at h ⊢;
      try { apply hf },
      { have h₂ := hf 0, cases f 0 with x xs,
        { exact (h₂ rfl).elim },
        { dsimp at *, simp at h, rw length_eq_zero at h, exact h }}},
    have hn₁ : v (flatten f' hf' n)  v (flatten f' hf' (n + 1)),
    { apply hn; clear hn n,
      { rintro n i j hi hj, subst f', dsimp at hi hj, rw f₁ at hi hj,
        split_ifs at hi hj with h h₁; dsimp at hi hj;
        try { apply hv1 _ _ _ hi hj },
        { apply hv1 0 _ _; apply mem_of_mem_tail; assumption }},
      { rintro n i hi j hj, subst f', dsimp at hi hj, dunfold f₁ at hi hj,
        split_ifs at hi hj with h₁ h₂; dsimp at *,
        { apply hv2; assumption },
        { apply hv2,
          { apply mem_of_mem_tail, assumption },
          { cases h₂ }},
        { cases h₂ },
        { replace hi := mem_of_mem_tail hi, apply hv2,
          { assumption },
          { rw h at hj, assumption }},
        { apply hv2; assumption }}},
    have h₁ :  n, flatten f hf (n + 1) = flatten f' hf' n,
    { intro k, rw [flatten, flatten, flatten'], congr, funext i, subst f',
      dsimp, simp_rw f₁, split_ifs with h₁ h₂; refl },
    rw [h₁, h₁], exact hn₁ },
end

David Wärn (Jan 07 2022 at 10:02):

You can also implement flatten using streams:

import data.stream.init

def head_of_ne_nil {α} : Π {ls : list α}, ls  []  α
| [] h        := (h rfl).elim
| (a :: ls) _ := a

/-- `flatten_aux (ls, s₀ :: s₁ :: ...) = ls ++ₛ s₀ ++ₛ s₁ ++ ...` -/
def flatten_aux {α} : list α × stream { ls : list α // ls  []}  stream α :=
stream.corec' $ λ p, match p with
  | (a :: ls, s) := (a, ls, s)
  | ([], s)      := (head_of_ne_nil s.head.2, s.head.val.tail, s.tail)
  end

def flatten {α} (s : stream { ls : list α // ls  [] }) : stream α := flatten_aux ([], s)

Rob Lewis (Jan 07 2022 at 14:58):

Fantastic, thanks @David Wärn !

Rob Lewis (Jan 07 2022 at 14:59):

And thanks again @Patrick Johnson :smile:


Last updated: Dec 20 2023 at 11:08 UTC