Recursive function that deals with "ref" in SML

49 Views Asked by At

This is my first recursive function; I'm new to the programming world. So, the code does really confuse me. I discovered that SML doesn't allow variable updating. So I learned to use ref, and that code was the result. But it is still wrong. Please tell me what's wrong with my code.

I expect this function to return how many times the second parameter appears as the second integer in each tuple of the list that is the first parameter.

fun number_in_month (lst: (int*int*int) list, month: int) =
  let 
    val dates = ref 0 : int ref;
  in 
    if null lst then 
      !dates
    else 
      let      
        val tl_ans = tl lst;
        val hd_ans = hd lst; 
      in
        let
          fun add_1(date : int*int*int) =
            if #2 date = month then (
              dates := !dates + 1; 
              !dates
            )
            else 
              !dates;
         in
           add_1(hd_ans) 
         end
          
         number_in_month(tl_ans, month);
       end 
2

There are 2 best solutions below

1
molbdnilo On BEST ANSWER

You should not be using ref, :=, or !.

The number of times it occurs is

  • If the list is empty, it is zero
  • Otherwise:
    • If the first element matches, it is one more than the number of occurrences in the list's tail
    • Otherwise, it is just the number of occurrences in the list's tail

This is a very straightforward recursion with two conditionals, and no updating of variables.
Translating to SML left as an exercise.
(Look at, and read about, more examples of list recursion.)

0
Chris On

Fundamentally all recursion identifies one (or more) base cases where recursion is no longer needed, and then handles other cases by converging on a base case.

If we want to add all numbers from a given int to zero, and we can assume negatives won't be used, it might look like:

fun add(0) = 0
  | add(n) = n + add(n - 1)

We have identified our base case when a zero is passed in as the argument. We've then had our other case recursively call itself, updating the data to converge on zero by subtracting one.

For lists, the base case is an empty list ([]) and we converge on that base case by calling a function on the tail of the list.

Consider as an example:

fun iter f [] = ()
  | iter f (x::xs) = (f x; iter f xs);

Where:

iter (fn x => print (Int.toString x ^ "\n")) [1,2,3,4,5];

Will print:

1
2
3
4
5

Consider how you could write a function filter which returns elements of a list which fulfill a predicate function. The base case is simple, so it is written below.

fun filter pred ([]) = []
  | filter pred (x::xs) = ...