Zulip Chat Archive

Stream: lean4

Topic: Beginner question on zip exercise, plus some monoids


DSB (Apr 22 2024 at 18:41):

Hello, friends. Just started learning Lean. I'm going through the Functional Programming book. I was just faced with the following exercise:

Write a function zip that combines two lists into a list of pairs. The resulting list should be as long as the shortest input list. Start the definition with def zip {α β : Type} (xs : List α) (ys : List β) : List (α × β) :=.

I'm having a hard time solving this one. It is not clear to me how to use pattern-matching in both lists at once. Here is my attempt, but it is failing to terminate:

def myzip {α β : Type} (xs : List α) (ys : List β) : List (α × β) :=
  match (xs, ys) with
  | ([], _) => []
  | (_, []) => []
  | (x :: xs', y :: ys') => (x, y) :: myzip xs' ys'

Also, a more advanced question. Is it possible to adapt this zip function to work only on Monoids, such as whenever one gets an empty list, the zip should use the neutral element. For example:

#eval monoidzip [1,2,3] [1,2]

Should return [(1,1),(2,2),(3,0)] where 0 would be the neutral element. Similarly, if instead of Natural numbers I had strings, I could define the monoid with the empty string.

𝚠𝚘𝚓𝚌𝚒𝚎𝚌𝚑 𝚗𝚊𝚠𝚛𝚘𝚌𝚔𝚒 (Apr 22 2024 at 19:44):

To your first question: here is a version that Lean can show terminating. Talk about 'spot the difference'!

def myzip {α β : Type} (xs : List α) (ys : List β) : List (α × β) :=
  match xs, ys with
  | [], _ => []
  | _, [] => []
  | x :: xs', y :: ys' => (x, y) :: myzip xs' ys'

𝚠𝚘𝚓𝚌𝚒𝚎𝚌𝚑 𝚗𝚊𝚠𝚛𝚘𝚌𝚔𝚒 (Apr 22 2024 at 19:48):

And for the second one

class Zero.{u} (α : Type u) where -- This is defined in mathlib
  zero : α
open Zero

def myzip {α β : Type} [Zero α] [Zero β] (xs : List α) (ys : List β) : List (α × β) :=
  match xs, ys with
  | [], [] => []
  | [], y :: ys' => (zero, y) :: myzip [] ys'
  | x :: xs', [] => (x, zero) :: myzip xs' []
  | x :: xs', y :: ys' => (x, y) :: myzip xs' ys'

instance : Zero Nat := 0

#eval myzip [1,2,3] [1,2] -- [(1, 1), (2, 2), (3, 0)]

Note that you don't actually need a monoid since nothing is being combined; you just need a neutral element. I chose zero here.

DSB (Apr 22 2024 at 19:50):

Wojciech Nawrocki said:

And for the second one

class Zero.{u} (α : Type u) where -- This is defined in mathlib
  zero : α
open Zero

def myzip {α β : Type} [Zero α] [Zero β] (xs : List α) (ys : List β) : List (α × β) :=
  match xs, ys with
  | [], [] => []
  | [], y :: ys' => (zero, y) :: myzip [] ys'
  | x :: xs', [] => (x, zero) :: myzip xs' []
  | x :: xs', y :: ys' => (x, y) :: myzip xs' ys'

instance : Zero Nat := 0

#eval myzip [1,2,3] [1,2] -- [(1, 1), (2, 2), (3, 0)]

Note that you don't actually need a monoid since nothing is being combined; you just need a neutral element. I chose zero here.

Thanks! Yeah, I know I don't need a monoid for the example at hand. I was just anticipating, cause I'll be needing monoids in the applications I'm coding.

Kevin Buzzard (Apr 22 2024 at 20:05):

(In lean the identity element of a Monoid is always 1 not 0)

James Sully (May 02 2024 at 02:35):

There's an open issue for this:
https://github.com/leanprover/fp-lean/issues/118


Last updated: May 02 2025 at 03:31 UTC