Zip and Unzip
zip
zip pairs up corresponding elements from two lists into a single list of pairs. If the lists differ in length, surplus elements from the longer list are silently discarded.
let rec zip xs ys =
match xs, ys with
| (x::xs, y::ys) -> (x, y) :: zip xs ys
| _ -> []
Type: 'a list -> 'b list -> ('a * 'b) list
Key points:
- The wildcard pattern
_matches anything and signals that the second clause discards its arguments. Using_is clearer than a named variable when the value is not used. - Pattern order matters: the first clause
(x::xs, y::ys)is tried before the wildcard. Only when at least one list is empty does the second clause fire. If the clauses were reversed,zipwould always return[].
unzip
unzip reverses the operation, taking a list of pairs and returning a pair of lists:
let rec unzip = function
| [] -> ([], [])
| (x, y)::pairs ->
let xs, ys = unzip pairs in
(x::xs, y::ys)
Type: ('a * 'b) list -> 'a list * 'b list
The local let binding let xs, ys = unzip pairs in ... destructures the result of the recursive call. In general, let P = E₁ in E₂ matches pattern P against the value of expression E₁ and binds the variables in P within E₂.
conspair helper
An alternative version of unzip extracts the destructuring into a helper function:
let conspair ((x, y), (xs, ys)) = (x::xs, y::ys)
let rec unzip = function
| [] -> ([], [])
| xy :: pairs -> conspair (xy, unzip pairs)
conspair takes a pair ((x, y), (xs, ys)) and returns (x::xs, y::ys). While not every local binding can be eliminated this cleanly, the pattern of factoring out helper functions is a useful technique for keeping code readable.