2

I am having two lists in SML, lets say list A [(a,b,c),(d,e,f)] and list B [b,e]. I want to count how many occurrence of each item in B that matches the second element of each triple in A. The output should be 2. Because b and e each occurs once in A.

This is my code so far but my counter is always set to 0 when I move from one element to another in B. I know in Java this will just be a simple double for loop.

fun number_in_months (d : (int * int * int ) list, m : (int) list) = 
    if null m then 0 
    else if null d then number_in_months(d, tl m)
    else if (#2(hd d)) = (hd m) then 1 + number_in_months (tl d, m)
    else number_in_months(tl d, m)
sshine
  • 15,635
  • 1
  • 41
  • 66
fanbondi
  • 1,035
  • 5
  • 18
  • 37
  • You really wan't to use pattern matching instead of testing for empty lists with `null`. This way you don't need to explicitly take the head and tail of the list either, as you would have already pattern matched them into e.g. (x::xs) and (y::ys) – Jesper.Reenberg Jan 16 '13 at 00:45

2 Answers2

5

The code is not accumulating a value between recursive calls. There may be other logic errors too.

Accumulating a value using recursion and functions is a common pattern which you can read more about here. It's essence is to deconstruct a list using head and tail until the list is empty and accumulate some value at each call. The sum function below is a simple example to show this. This could be adapted to your example to accumulate acc when b or e are found in list A.

fun sum(numbers: (int) list) =
  let fun sumR(numbers: (int) list, acc: int) =
    if null numbers
    then acc
    else
      sumR(tl numbers, hd numbers + acc)
  in
    sumR(numbers, 0)
  end

Running on [1,2,3] gives:

val sum = fn : int list -> int
- sum([1,2,3]);
val it = 6 : int

Note I am intentionally vague with this answer since this is a question regarding Coursera homework for the Programming Languages class.

Brian
  • 20,195
  • 6
  • 34
  • 55
  • Thanks for your answer, if I run the code on [(1,2,3)(2,2,3)(4,2,3)],[2,4] I got the answer 6 which is the sum of the middle 2's. So its accumulating the sum the problem is its should further continue and check for 4 in the middle elements of the first list and if 4 is found 6 should be updated to 7. – fanbondi Jan 16 '13 at 00:17
  • 1
    For `number_in_months` the answer for the input `[(1,2,3), (2,2,3),(4,2,3)],[2,4]` should be 3 since every `date` has `2` as the month and `2` is in the list of `months` – Brian Jan 16 '13 at 00:25
  • yeah you are right it should have been 3 not 6, thats my mistake. – fanbondi Jan 16 '13 at 08:10
2

As you mention, it would be a nested/double loop in any imperative programming language. What you are actually missing is the second loop.

Your "inner" loop goes through all elements of d, and when this is done, your "outer" loop tries to pop the top element of m and start all over, as seen from this line of your code:

else if null d then number_in_months(d, tl m)

However as you can see, you have just tested the list d to be empty and you supply this (exact same list) to your recursive call on the tail of m, which will then fall in this same case for each successive call until m is also empty and you return 0.

Thus what you are missing is to "keep a copy" of the original input list m. This can be done in various ways, but an inner (helper) function is properly the most used one and it even "looks" like a nested loop

fun number_in_months (d, m) =
    let
      fun nim' ([], y::ys) = nim (d, ys)                 (* 1 *)
        | nim' (_, []) = 0                               (* 2 *)
        | nim' ((_, x2, _) :: xs, yss as (y::ys)) = ...  (* 3 *)
    in
      nim'(d, m)
    end

Using pattern matching the above code gets much simpler and less error prone. In case 1, the "inner" loop has gone through all elements in d, thus the recursive call using d from the outer function which is not changed at any time. In case 2, the "outer" loop has gone through all elements of m and we return 0 (the neutral element of addition). In case 3 we do the actual work. Here pattern matching is used such that we don't need to enforce the type of the argument and we don't need to pull out the 2nd element of the triple, we already have it in the variable x2. All that is needed is to do the computation and make a recursive call with xs and yss.

When doing it this way, the inner (helper) function is using a "copy" of the original input list d and stepping through its elements (potentially modifying it), but we always got a reference to the original input list, which we can use if/when needed.

Jesper.Reenberg
  • 5,944
  • 23
  • 31
  • Thanks for the explanation, the error you pointed was very important and I finally got what I want. – fanbondi Jan 16 '13 at 15:18