Skip to content
This repository was archived by the owner on Jul 24, 2024. It is now read-only.

Commit bc77a23

Browse files
committed
feat(data/list/*): add left- and right-biased versions of map₂ and zip (#4512)
When given lists of different lengths, `map₂` and `zip` truncate the output to the length of the shorter input list. This commit adds variations on `map₂` and `zip` whose output is always as long as the left/right input.
1 parent d3d70f1 commit bc77a23

File tree

2 files changed

+354
-1
lines changed

2 files changed

+354
-1
lines changed

src/data/list/basic.lean

Lines changed: 219 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1402,6 +1402,13 @@ by cases l; refl
14021402
theorem map₂_nil (f : α → β → γ) (l : list α) : map₂ f l [] = [] :=
14031403
by cases l; refl
14041404

1405+
@[simp] theorem map₂_flip (f : α → β → γ) :
1406+
∀ as bs, map₂ (flip f) bs as = map₂ f as bs
1407+
| [] [] := rfl
1408+
| [] (b :: bs) := rfl
1409+
| (a :: as) [] := rfl
1410+
| (a :: as) (b :: bs) := by { simp! [map₂_flip], refl }
1411+
14051412
/-! ### take, drop -/
14061413
@[simp] theorem take_zero (l : list α) : take 0 l = [] := rfl
14071414

@@ -4024,7 +4031,218 @@ lemma choose_property (hp : ∃ a, a ∈ l ∧ p a) : p (choose p l hp) := (choo
40244031

40254032
end choose
40264033

4027-
-- A jumble of lost lemmas:
4034+
/-! ### map₂_left' -/
4035+
4036+
section map₂_left'
4037+
4038+
-- The definitional equalities for `map₂_left'` can already be used by the
4039+
-- simplifie because `map₂_left'` is marked `@[simp]`.
4040+
4041+
@[simp] theorem map₂_left'_nil_right (f : α → option β → γ) (as) :
4042+
map₂_left' f as [] = (as.map (λ a, f a none), []) :=
4043+
by cases as; refl
4044+
4045+
end map₂_left'
4046+
4047+
/-! ### map₂_right' -/
4048+
4049+
section map₂_right'
4050+
4051+
variables (f : option α → β → γ) (a : α) (as : list α) (b : β) (bs : list β)
4052+
4053+
@[simp] theorem map₂_right'_nil_left :
4054+
map₂_right' f [] bs = (bs.map (f none), []) :=
4055+
by cases bs; refl
4056+
4057+
@[simp] theorem map₂_right'_nil_right :
4058+
map₂_right' f as [] = ([], as) :=
4059+
rfl
4060+
4061+
@[simp] theorem map₂_right'_nil_cons :
4062+
map₂_right' f [] (b :: bs) = (f none b :: bs.map (f none), []) :=
4063+
rfl
4064+
4065+
@[simp] theorem map₂_right'_cons_cons :
4066+
map₂_right' f (a :: as) (b :: bs) =
4067+
let rec := map₂_right' f as bs in
4068+
(f (some a) b :: rec.fst, rec.snd) :=
4069+
rfl
4070+
4071+
end map₂_right'
4072+
4073+
/-! ### zip_left' -/
4074+
4075+
section zip_left'
4076+
4077+
variables (a : α) (as : list α) (b : β) (bs : list β)
4078+
4079+
@[simp] theorem zip_left'_nil_right :
4080+
zip_left' as ([] : list β) = (as.map (λ a, (a, none)), []) :=
4081+
by cases as; refl
4082+
4083+
@[simp] theorem zip_left'_nil_left :
4084+
zip_left' ([] : list α) bs = ([], bs) :=
4085+
rfl
4086+
4087+
@[simp] theorem zip_left'_cons_nil :
4088+
zip_left' (a :: as) ([] : list β) = ((a, none) :: as.map (λ a, (a, none)), []) :=
4089+
rfl
4090+
4091+
@[simp] theorem zip_left'_cons_cons :
4092+
zip_left' (a :: as) (b :: bs) =
4093+
let rec := zip_left' as bs in
4094+
((a, some b) :: rec.fst, rec.snd) :=
4095+
rfl
4096+
4097+
end zip_left'
4098+
4099+
/-! ### zip_right' -/
4100+
4101+
section zip_right'
4102+
4103+
variables (a : α) (as : list α) (b : β) (bs : list β)
4104+
4105+
@[simp] theorem zip_right'_nil_left :
4106+
zip_right' ([] : list α) bs = (bs.map (λ b, (none, b)), []) :=
4107+
by cases bs; refl
4108+
4109+
@[simp] theorem zip_right'_nil_right :
4110+
zip_right' as ([] : list β) = ([], as) :=
4111+
rfl
4112+
4113+
@[simp] theorem zip_right'_nil_cons :
4114+
zip_right' ([] : list α) (b :: bs) = ((none, b) :: bs.map (λ b, (none, b)), []) :=
4115+
rfl
4116+
4117+
@[simp] theorem zip_right'_cons_cons :
4118+
zip_right' (a :: as) (b :: bs) =
4119+
let rec := zip_right' as bs in
4120+
((some a, b) :: rec.fst, rec.snd) :=
4121+
rfl
4122+
4123+
end zip_right'
4124+
4125+
/-! ### map₂_left -/
4126+
4127+
section map₂_left
4128+
4129+
variables (f : α → option β → γ) (as : list α)
4130+
4131+
-- The definitional equalities for `map₂_left` can already be used by the
4132+
-- simplifier because `map₂_left` is marked `@[simp]`.
4133+
4134+
@[simp] theorem map₂_left_nil_right :
4135+
map₂_left f as [] = as.map (λ a, f a none) :=
4136+
by cases as; refl
4137+
4138+
theorem map₂_left_eq_map₂_left' : ∀ as bs,
4139+
map₂_left f as bs = (map₂_left' f as bs).fst
4140+
| [] bs := by simp!
4141+
| (a :: as) [] := by simp!
4142+
| (a :: as) (b :: bs) := by simp! [*]
4143+
4144+
theorem map₂_left_eq_map₂ : ∀ as bs,
4145+
length as ≤ length bs →
4146+
map₂_left f as bs = map₂ (λ a b, f a (some b)) as bs
4147+
| [] [] h := by simp!
4148+
| [] (b :: bs) h := by simp!
4149+
| (a :: as) [] h := by { simp at h, contradiction }
4150+
| (a :: as) (b :: bs) h := by { simp at h, simp! [*] }
4151+
4152+
end map₂_left
4153+
4154+
/-! ### map₂_right -/
4155+
4156+
section map₂_right
4157+
4158+
variables (f : option α → β → γ) (a : α) (as : list α) (b : β) (bs : list β)
4159+
4160+
@[simp] theorem map₂_right_nil_left :
4161+
map₂_right f [] bs = bs.map (f none) :=
4162+
by cases bs; refl
4163+
4164+
@[simp] theorem map₂_right_nil_right :
4165+
map₂_right f as [] = [] :=
4166+
rfl
4167+
4168+
@[simp] theorem map₂_right_nil_cons :
4169+
map₂_right f [] (b :: bs) = f none b :: bs.map (f none) :=
4170+
rfl
4171+
4172+
@[simp] theorem map₂_right_cons_cons :
4173+
map₂_right f (a :: as) (b :: bs) = f (some a) b :: map₂_right f as bs :=
4174+
rfl
4175+
4176+
theorem map₂_right_eq_map₂_right' :
4177+
map₂_right f as bs = (map₂_right' f as bs).fst :=
4178+
by simp only [map₂_right, map₂_right', map₂_left_eq_map₂_left']
4179+
4180+
theorem map₂_right_eq_map₂ (h : length bs ≤ length as) :
4181+
map₂_right f as bs = map₂ (λ a b, f (some a) b) as bs :=
4182+
begin
4183+
have : (λ a b, flip f a (some b)) = (flip (λ a b, f (some a) b)) := rfl,
4184+
simp only [map₂_right, map₂_left_eq_map₂, map₂_flip, *]
4185+
end
4186+
4187+
end map₂_right
4188+
4189+
/-! ### zip_left -/
4190+
4191+
section zip_left
4192+
4193+
variables (a : α) (as : list α) (b : β) (bs : list β)
4194+
4195+
@[simp] theorem zip_left_nil_right :
4196+
zip_left as ([] : list β) = as.map (λ a, (a, none)) :=
4197+
by cases as; refl
4198+
4199+
@[simp] theorem zip_left_nil_left :
4200+
zip_left ([] : list α) bs = [] :=
4201+
rfl
4202+
4203+
@[simp] theorem zip_left_cons_nil :
4204+
zip_left (a :: as) ([] : list β) = (a, none) :: as.map (λ a, (a, none)) :=
4205+
rfl
4206+
4207+
@[simp] theorem zip_left_cons_cons :
4208+
zip_left (a :: as) (b :: bs) = (a, some b) :: zip_left as bs :=
4209+
rfl
4210+
4211+
theorem zip_left_eq_zip_left' :
4212+
zip_left as bs = (zip_left' as bs).fst :=
4213+
by simp only [zip_left, zip_left', map₂_left_eq_map₂_left']
4214+
4215+
end zip_left
4216+
4217+
/-! ### zip_right -/
4218+
4219+
section zip_right
4220+
4221+
variables (a : α) (as : list α) (b : β) (bs : list β)
4222+
4223+
@[simp] theorem zip_right_nil_left :
4224+
zip_right ([] : list α) bs = bs.map (λ b, (none, b)) :=
4225+
by cases bs; refl
4226+
4227+
@[simp] theorem zip_right_nil_right :
4228+
zip_right as ([] : list β) = [] :=
4229+
rfl
4230+
4231+
@[simp] theorem zip_right_nil_cons :
4232+
zip_right ([] : list α) (b :: bs) = (none, b) :: bs.map (λ b, (none, b)) :=
4233+
rfl
4234+
4235+
@[simp] theorem zip_right_cons_cons :
4236+
zip_right (a :: as) (b :: bs) = (some a, b) :: zip_right as bs :=
4237+
rfl
4238+
4239+
theorem zip_right_eq_zip_right' :
4240+
zip_right as bs = (zip_right' as bs).fst :=
4241+
by simp only [zip_right, zip_right', map₂_right_eq_map₂_right']
4242+
4243+
end zip_right
4244+
4245+
/-! ### Miscellaneous lemmas -/
40284246

40294247
theorem ilast'_mem : ∀ a l, @ilast' α a l ∈ a :: l
40304248
| a [] := or.inl rfl

src/data/list/defs.lean

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -703,4 +703,139 @@ def slice {α} : ℕ → ℕ → list α → list α
703703
| (succ n) m [] := []
704704
| (succ n) m (x :: xs) := x :: slice n m xs
705705

706+
/--
707+
Left-biased version of `list.map₂`. `map₂_left' f as bs` applies `f` to each
708+
pair of elements `aᵢ ∈ as` and `bᵢ ∈ bs`. If `bs` is shorter than `as`, `f` is
709+
applied to `none` for the remaining `aᵢ`. Returns the results of the `f`
710+
applications and the remaining `bs`.
711+
712+
```
713+
map₂_left' prod.mk [1, 2] ['a'] = ([(1, some 'a'), (2, none)], [])
714+
715+
map₂_left' prod.mk [1] ['a', 'b'] = ([(1, some 'a')], ['b'])
716+
```
717+
-/
718+
@[simp] def map₂_left' (f : α → option β → γ) : list α → list β → (list γ × list β)
719+
| [] bs := ([], bs)
720+
| (a :: as) [] :=
721+
((a :: as).map (λ a, f a none), [])
722+
| (a :: as) (b :: bs) :=
723+
let rec := map₂_left' as bs in
724+
(f a (some b) :: rec.fst, rec.snd)
725+
726+
/--
727+
Right-biased version of `list.map₂`. `map₂_right' f as bs` applies `f` to each
728+
pair of elements `aᵢ ∈ as` and `bᵢ ∈ bs`. If `as` is shorter than `bs`, `f` is
729+
applied to `none` for the remaining `bᵢ`. Returns the results of the `f`
730+
applications and the remaining `as`.
731+
732+
```
733+
map₂_right' prod.mk [1] ['a', 'b'] = ([(some 1, 'a'), (none, 'b')], [])
734+
735+
map₂_right' prod.mk [1, 2] ['a'] = ([(some 1, 'a')], [2])
736+
```
737+
-/
738+
def map₂_right' (f : option α → β → γ) (as : list α) (bs : list β) : (list γ × list α) :=
739+
map₂_left' (flip f) bs as
740+
741+
/--
742+
Left-biased version of `list.zip`. `zip_left' as bs` returns the list of
743+
pairs `(aᵢ, bᵢ)` for `aᵢ ∈ as` and `bᵢ ∈ bs`. If `bs` is shorter than `as`, the
744+
remaining `aᵢ` are paired with `none`. Also returns the remaining `bs`.
745+
746+
```
747+
zip_left' [1, 2] ['a'] = ([(1, some 'a'), (2, none)], [])
748+
749+
zip_left' [1] ['a', 'b'] = ([(1, some 'a')], ['b'])
750+
751+
zip_left' = map₂_left' prod.mk
752+
753+
```
754+
-/
755+
def zip_left' : list α → list β → list (α × option β) × list β :=
756+
map₂_left' prod.mk
757+
758+
/--
759+
Right-biased version of `list.zip`. `zip_right' as bs` returns the list of
760+
pairs `(aᵢ, bᵢ)` for `aᵢ ∈ as` and `bᵢ ∈ bs`. If `as` is shorter than `bs`, the
761+
remaining `bᵢ` are paired with `none`. Also returns the remaining `as`.
762+
763+
```
764+
zip_right' [1] ['a', 'b'] = ([(some 1, 'a'), (none, 'b')], [])
765+
766+
zip_right' [1, 2] ['a'] = ([(some 1, 'a')], [2])
767+
768+
zip_right' = map₂_right' prod.mk
769+
```
770+
-/
771+
def zip_right' : list α → list β → list (option α × β) × list α :=
772+
map₂_right' prod.mk
773+
774+
/--
775+
Left-biased version of `list.map₂`. `map₂_left f as bs` applies `f` to each pair
776+
`aᵢ ∈ as` and `bᵢ ‌∈ bs`. If `bs` is shorter than `as`, `f` is applied to `none`
777+
for the remaining `aᵢ`.
778+
779+
```
780+
map₂_left prod.mk [1, 2] ['a'] = [(1, some 'a'), (2, none)]
781+
782+
map₂_left prod.mk [1] ['a', 'b'] = [(1, some 'a')]
783+
784+
map₂_left f as bs = (map₂_left' f as bs).fst
785+
```
786+
-/
787+
@[simp] def map₂_left (f : α → option β → γ) : list α → list β → list γ
788+
| [] _ := []
789+
| (a :: as) [] := (a :: as).map (λ a, f a none)
790+
| (a :: as) (b :: bs) := f a (some b) :: map₂_left as bs
791+
792+
/--
793+
Right-biased version of `list.map₂`. `map₂_right f as bs` applies `f` to each
794+
pair `aᵢ ∈ as` and `bᵢ ‌∈ bs`. If `as` is shorter than `bs`, `f` is applied to
795+
`none` for the remaining `bᵢ`.
796+
797+
```
798+
map₂_right prod.mk [1, 2] ['a'] = [(some 1, 'a')]
799+
800+
map₂_right prod.mk [1] ['a', 'b'] = [(some 1, 'a'), (none, 'b')]
801+
802+
map₂_right f as bs = (map₂_right' f as bs).fst
803+
```
804+
-/
805+
def map₂_right (f : option α → β → γ) (as : list α) (bs : list β) :
806+
list γ :=
807+
map₂_left (flip f) bs as
808+
809+
/--
810+
Left-biased version of `list.zip`. `zip_left as bs` returns the list of pairs
811+
`(aᵢ, bᵢ)` for `aᵢ ∈ as` and `bᵢ ∈ bs`. If `bs` is shorter than `as`, the
812+
remaining `aᵢ` are paired with `none`.
813+
814+
```
815+
zip_left [1, 2] ['a'] = [(1, some 'a'), (2, none)]
816+
817+
zip_left [1] ['a', 'b'] = [(1, some 'a')]
818+
819+
zip_left = map₂_left prod.mk
820+
```
821+
-/
822+
def zip_left : list α → list β → list (α × option β) :=
823+
map₂_left prod.mk
824+
825+
/--
826+
Right-biased version of `list.zip`. `zip_right as bs` returns the list of pairs
827+
`(aᵢ, bᵢ)` for `aᵢ ∈ as` and `bᵢ ∈ bs`. If `as` is shorter than `bs`, the
828+
remaining `bᵢ` are paired with `none`.
829+
830+
```
831+
zip_right [1, 2] ['a'] = [(some 1, 'a')]
832+
833+
zip_right [1] ['a', 'b'] = [(some 1, 'a'), (none, 'b')]
834+
835+
zip_right = map₂_right prod.mk
836+
```
837+
-/
838+
def zip_right : list α → list β → list (option α × β) :=
839+
map₂_right prod.mk
840+
706841
end list

0 commit comments

Comments
 (0)