Lesson 10 · Recursion: filter, take, drop

Keep, Drop, and Slice

Four functions. No loops, no filter, no slicing, no built-ins except comparison. All return new lists; the original is never mutated.

Recursion recap (standalone): a recursive function calls itself on a smaller input. For lists the base case is the empty list []; the recursive case peels off the first element xs[0] and recurses on the rest xs[1:]. A predicate is a function that takes a value and returns a boolean (True/False).


Part 1 — keep(xs, pred)

Return a new list containing only the elements for which pred returns True.

keep([1,2,3,4,5], lambda x: x % 2 == 0)   # [2, 4]
keep(["cat","dog","ox"], lambda s: len(s) > 2)  # ["cat","dog"]
keep([], lambda x: x > 0)                 # []

Part 2 — take(n, xs)

Return the first n elements of xs (or the whole list if n ≥ len(xs)).

take(3, [10, 20, 30, 40])   # [10, 20, 30]
take(0, [10, 20, 30])       # []
take(5, [1, 2])             # [1, 2]

There are two base cases to handle — figure out what they are.


Part 3 — drop(n, xs)

Return the list with the first n elements removed.

drop(2, [10, 20, 30, 40])   # [30, 40]
drop(0, [10, 20, 30])       # [10, 20, 30]
drop(5, [1, 2])             # []

Part 4 (stretch) — nth(n, xs)

Return the element at position n (0-based), or None if out of range.

nth(0, [10, 20, 30])   # 10
nth(2, [10, 20, 30])   # 30
nth(3, [10, 20, 30])   # None
nth(0, [])             # None

Notice the shape of drop and think about how nth relates to it.

Work out your answer first — then check it.