To start, let me share that I'm a complete novice at formal programming and have decided to start learning after many years of working in IT from the administrative side. So I am starting from scratch and hopefully unlearning any bad habits I've picked up over the years.
To help myself out, I've started a course online that has some homework. I've promised myself to not cheat, but I'm struggling on this problem and would like some help. I did see a similar post that was answered, but the answer doesn't make sense to me, so I'm asking again.
Here is the assignment ...
" Write a function "number_in_months" that takes a list of dates and a list of months (i.e., an int list) and returns the number of dates in the list of dates that are in any of the months in the list of months. Assume the list of months has no number repeated. Hint: Use your answer to the previous problem. "
You'll notice that it refers to the previous problem. I've shared it below.
" Write a function "number_in_month" that takes a list of dates and a month (i.e., an int) and returns how many dates in the list are in the given month. "
I was able to solve the previous problem with this code.
fun number_in_month (dates : (int * int * int) list , month : int ) =
if null dates
then 0
else
if ( #2 (hd dates) = month )
then 1 + number_in_month (tl dates , month)
else number_in_month (tl dates , month)
In my REPL val test2 = number_in_month ([(2012,2,28),(2013,12,1)],2) = 1
tests true
So I've re-read the material and re-watched the videos a few times and still haven't gotten to an understanding on how to solve the problem. I can get it all to type check correctly but then get an uncaught exception EMPTY
I can recurse the "dates" list against the hd of the "months" list with no problem but to move on to the next value in the "months" list is killing me. So far I have tried many ways, but I'm stumped, and I'm feeling not just a little stupid. ;-)
fun number_in_months (dates : (int * int * int) list , months : int list ) =
if null dates
then 0
else
if (#2 (hd dates) = (hd months))
then 1 + number_in_months ( tl dates , months )
else
????
It may be that I solved the first problem in such a way that it is throwing me off with the second. I'm open to any ideas, guidance, or clues. All will be appreciated
The more idiomatic way to handle empty lists and accessing elements of tuples in SML is pattern matching. If we consider your number_in_month
function, we'd look at something like the following. If the list of dates is empty, it doesn't matter what month we're looking for, so we represent it with _
.
Otherwise we consider the first tuple (but we only need to bind a name to the second element representing the month) and bind a name to the tail of the list. We count the tail of the list, and then if the current one is a match, add 1
.
fun number_in_month([], _) = 0
| number_in_month(((_, mon, _)::other_dates), month) =
let
val count_remaining = number_in_month(other_dates, month)
in
(if mon = month then 1 else 0) + count_remaining
end;
For your numbers_in_months
it seems you want to map over the list of months. List.map
provides exactly this, and can directly use your prior work.
fun numbers_in_months(dates, months) =
List.map (fn m => number_in_month(dates, m)) months;
numbers_in_months([(2012, 2, 28), (2013, 12, 1)], [4, 12, 2]);
Returns:
[0, 1, 1]
map
can be implemented trivially, even without the niceties of pattern-matching.
fun map f lst =
if null lst then []
else f (hd lst) :: map f (tl lst)