Search code examples
sml

This standard ML function gives me `unhandled exception: Match`. Can someone point me in the right direction to solve the problem?


I am trying to write a Standard ML function called 'select' that receives a list of integers and a function. The passed function receives an integer and returns a boolean (ex, fun isOdd 3 -> true). The function is to go through each integer in the list and append them to a new list if the given function returns true for that integer. So select [1,2,3,4,5] isOdd will return [1,3,5]. This is the code I've got:

fun select (l : int list, f:int -> bool) = 
    let val newlist : int list = []
        fun recurse (x::xs) =
            if f(x)
                then newlist :: [x] :: recurse(xs)
            else
                newlist :: recurse(xs)
    in
        recurse(l : int list)
    end

Solution

  • recurse doesn't have a case for the empty list, so it will fail when you reach the empty list.

    Let's fix that:

    fun select (l : int list, f:int -> bool) = 
        let val newlist : int list = []
            fun recurse [] = []
              | recurse (x::xs) =
                if f(x)
                    then newlist :: [x] :: recurse(xs)
                else
                    newlist :: recurse(xs)
        in
            recurse l
        end
    

    and test:

    - select ([1,2,3,4], fn x => x mod 2 <> 0);
    val it = [[],[1],[],[],[3],[]] : int list list
    - select ([1], fn x => true);
    val it = [[],[1]] : int list list
    - select ([1], fn x => false);
    val it = [[]] : int list list
    

    That's no good – we want [1,3], [1], and [].

    The type of your function is

    val select = fn : int list * (int -> bool) -> int list list
    

    The int list list result is wrong; it should be int list.
    This happens because the first element of the result from recurse is an int list – the empty newlist – so the result must be an int list list.

    Fixing the problem gives

    fun select (l : int list, f:int -> bool) = 
        let fun recurse [] = []
              | recurse (x::xs) =
                if f x
                then x :: recurse xs
                else recurse xs 
        in
            recurse l
        end
    

    Note that the type constraints are fairly pointless; if you remove them you will get a much more useful polymorphic function.