Search code examples
listfunctional-programmingsmlsmlnj

How do I pair the elements in the first list with all the elements in a second list in SML?


The Problem Statement: Write a function pair that takes two lists of integers and generates a list of pairs, where each pair is a combination of each element from each list.

For example, pair ([1,2], [3,4,5]) should return

[(1,3), (1,4), (1,5), (2,3), (2,4), (2,5)].

My work so far:

-fun pair(a:int list, b:int list) = if null a then nil else if null b then nil else (hd a, hd b)::pair(a, tl b)@pair(tl a, b);

val pair = fn : int list * int list -> (int * int) list

-pair([1,2],[3,4,5]);

val it = [(1,3),(1,4),(1,5),(2,5),(2,4),(2,5),(2,3),(2,4),(2,5)]

I've tried to trace the function to find out why the (2,5), (2,4), (2,5) are showing up but I'm not seeing it clearly still.

It seems straightforward enough but I can't seem to get the last bits ironed out. Some assistance pinpointing why those elements are being added in the middle would be of help.

Thanks.
Peter


Solution

  • The main problem is that you're recursing over both lists.

    If you look at your example,

    pair ([1,2], [3,4,5]) -> [(1,3), (1,4), (1,5), (2,3), (2,4), (2,5)]
    

    you will see that it has two sublists,

    [(1,3), (1,4), (1,5)]
    [(2,3), (2,4), (2,5)]
    

    where the first consists of pairs formed from the first element of [1,2] and every element of [3,4,5], and the second is the second element of [1,2] also paired with every element of [3,4,5].
    Note that each sublist contains all of [3,4,5] but only one element of [1,2] - the first is the same as pair ([1], [3,4,5]) and the second is pair ([2], [3,4,5]) - so you should only need to recurse over the first list.

    You can create such a list like this:

    • If any input list is empty, the result is empty.
    • Otherwise:
      1. Take the first element of a and pair it with every element of b in a list (hint: think about map.)
      2. Recursively make pairs from the tail of a and all of b.
      3. Combine the results of 1 and 2.

    With pattern matching:

    fun pair ([], _) = []
      | pair (_, []) = []
      | pair (x::xs, ys) = <something involving x and ys, suitably combined with 'pairs (xs, ys)'> 
    

    It might help if you write step 1 as a separate function.