Search code examples
logicsml

Trying to write a function in SML


I am trying to write a function in sml that takes a list as its first argument and a number as its second. The result should be: greaterT [3,5,2,4,7]3; val it = [5,4,7] : int list

This is my work so far but doesn't work yet.

fun greaterT ([],k) = []
|   greaterT (a::x,k)
if a <= k
then x = [] 
else greaterT(x,k);

Solution

  • You are getting into problems because the then branch of the if expression is trying to do something which doesn't make sense:

    x = []
    

    You can't re-assign values to already bound identifiers in Standard ML, although you can achieve mutability using refs, but they're not needed in this case.

    In your case, what the required function conceptually does, is to look at the first element of a list, decide whether to keep it in the final result by comparing it with k and then recurse on the rest of list:

    fun greaterT ([], k) = []
      | greaterT (a :: rest, k) =
          if a <= k then
            a :: greaterT (rest, k)
          else
            greaterT (rest, k)
    

    The above isn't a good solution, though, because the first recursive call isn't in a tail position, so the compiler can't optimize the generated code (for reasons I won't discuss here; there are plenty of questions about tail-call optimizations on StackOverflow).

    So, a better version would use an extra parameter in which it accumulates elements that satisfy the <= predicate.

    fun greaterTailRec ([], k, result) = List.rev result
      | greaterTailRec (a :: rest, k) =
          if a <= k then
            greaterTailRec (rest, k, result)
          else
            greaterTailRec (rest, k, a :: result)
    
    fun greaterT (list, k) = greaterTailRec (list, k, [])
    

    We can go a step further and generalize greaterTailRec by replacing the specifics, which in this particular case is the comparison call <=, to a more general call to a function that takes an element of the list as argument and returns a bool. Thus, we'll end up with a generally useful function called filter:

    fun filter predicate list =
      let
        fun recur ([], result) = List.rev result
          | recur (a :: rest, result) =
              if predicate a then
                recur (rest, a :: result)
              else
                recur (rest, result)
      in
        recur (list, [])
      end
    
    fun greaterT (list, k) =
      filter (fn a => a >= k) list
    

    The filter helper function is already defined on the List structure, so your initial function can be more concisely expressed as:

    fun greaterT (list, k) =
      List.filter (fn a => a >= k) list