1

I have been trying to count elements in a list of integer 3-tuples, that equals a given integer using SML, but it's not working. Can anyone help me figure out what's wrong with the below code or straighten it up for me?

 fun number_in_month(x : int*int*int list, m: int) =
    if null x then 0

         else
           let fun inc x = x + 1;
         in
             val counter = 0;
             if m = #2 (hd x) andalso m > 0 then inc counter
            number_in_month((tl x), m)
           `  else
             number_in_month((tl x), m)
        end

This function is supposed to return the number of times m equals to the second element of each tuple in the list.

sshine
  • 15,635
  • 1
  • 41
  • 66
guthik
  • 404
  • 1
  • 7
  • 15

2 Answers2

5

Clearly you have a hard time to let go of your imperative thinking.

Let me try and address some of your issues

  • You should be using pattern matching instead of using null x, hd x and tl x. This also apply to decomposing tuples and records. For example

    fun number_in_month ((x1, x2, x3) :: xs, m) = ...
    

    or, since we don't ever use x1 and x3

    fun number_in_month ((_, x2, _) :: xs, m) = ...
    

    This way it is clearly seen that the first argument is a list of 3-tuples, and no type annotation is needed

    Also when you omit the explicit type annotation, which is the whole idea of having a type system that can infer them for you (see next point), then this code

    fun foo42 xs = map (fn x => #2 x) xs
    

    will give you some nasty errors on "unresolved flex record" (this error message is from SML/NJ)

    /tmp/sml20620PlF:105.5-105.44 Error: unresolved flex record
       (can't tell what fields there are besides #2)
    

    which is easily fixed by decomposing the 3-tuple

    fun foo42 xs = map (fn (_, x2, _) => x2) xs
    
  • Speaking of type annotations. They are (almost always) not needed, and they clutter up the readability of the code. Not to mention that they unnecessarily restricts the types you function may be used on.

    Also the type annotation you have given is erroneous according to what you really wan't. You should have places parenthesis around the int * int * int. Currently it is interpreted as a 3-tuple of two ints and an int list int * int * (int list).

    If you really insist in type annotating your function, then you can do it like this

    val number_in_month : (int * int * int) list * int -> int = 
        fn ([]            , m) => 0
         | ((_,x2,_) :: xs, m) => 42
    

    This is "almost" like Haskell, where the type is given just before the function declaration.

  • Try to be more consistent in they way you indent your code. That will give you better clarity. Here I'm specifically thinking of the way you have indented the else part end the in ... end part. The below part is clearly still erroneous in so many ways i can't begin to imagine, but it gives an idea as how to do it

    fun number_in_month(x : int*int*int list, m: int) =
        if null x then 0
        else
          let fun inc x = x + 1;
          in
            val counter = 0;
            if m = #2 (hd x) andalso m > 0 then
               inc counter
               number_in_month((tl x), m)
            else
               number_in_month((tl x), m)
          end
    
  • You can't declare a variable val counter = 0 inside the in ... end part of a let-expression. The semantics of a let-expression is

    let
      dec
    in
      exp_1; ...; exp_n
    end
    

    thus all declarations (function and value bindings, etc) must go in the let ... in part.

  • There is no need on earth to have an increment function, it just clutters the readability. Remember that SML uses single assignment, thus variables are immutable after they are declared.

  • The sequence-thing inside your nested if-expression

    inc counter
    number_in_month((tl x), m)
    

    makes absolutely no sense. The only way you can have more than one expression inside the then ... else part (actually any place, where a single expression is expected), is with a sequence (exp_1; ...; exp_n). However this is only usable when all but the last expression has side effect(s), as their results is ignored/thrown away

    - (print "Foo\n"; print "Bar\n"; 42);
    Foo
    Bar
    val it = 42 : int
    

If you search a bit here on SO, you will see that a quite similar question has recently been asked and answered. Though it differs in the the type of the last argument, you might still get some useful pointers.

All in all a solution might look like

fun number_in_month ([], _) = 0
  | number_in_month ((_,x2,_) :: xs, m) = 
    if x2 = m then
      1 + number_in_month(xs, m)
    else
      number_in_month(xs, m)

However since your problem is simpler than the previously stated one, you could easily use some of the higher-order functions from the list module in the basis library

fun number_in_month (xs, m) = length (List.filter (fn (_, x2, _) => x2 = m) xs)

Or even (arguably) simpler, by folding over the list and incrementing a variable along the way each time it matches

fun number_in_month (xs, m) = foldl (fn ((_, x2, _), b) => if x2 = m then b+1 else b) 0 xs
Community
  • 1
  • 1
Jesper.Reenberg
  • 5,944
  • 23
  • 31
  • Jesper.. you are on a roll today.. :) – brunsgaard Jan 20 '13 at 23:10
  • i saw the question but i couldn't understand the code. that is why i needed something i could understand. thanks alot really. the thing is, am new to functional programming but it seems like alot of fun. i dont know what the => does and i wanted a really recursive function because i dont know how folding works at all. but your ideas are really great. am gonna try them all out. tomorrow night, latest, i will let you know about the results if you mind. thanks by the way. alot – guthik Jan 21 '13 at 01:35
  • Howdy Jesper! Could help but notice - this question - http://stackoverflow.com/questions/14431526/sml-error-operator-and-operand-dont-agree-when-comparing-integer-in-list-to-an/14432683 - seems oddly similar :-) - and here's the fun part - our answers get similar towards the end :-D – Faiz Jan 21 '13 at 14:56
  • 1
    Yeah, it is the 3rd of time (4th with yours), that (basically) the "same" question has been asked. I guess there is only "one" way to do it, when doing it right :) – Jesper.Reenberg Jan 21 '13 at 23:12
  • This answer uses concepts not yet covered in the implied course. – bentaisan Sep 05 '20 at 04:34
  • The constraints are "do not use pattern matching". – bentaisan Oct 02 '20 at 02:47
  • @bentaisan, wow.. +7 years later, and that is the best you had to add to the comments. I don't know what implied course this was from, as it wasn't from any I have taken or taught. I answered the question given the facts that were presented, and if I recall my mental state, trying not to just spill out the solution to be copy/pasted in an assignmnet, as the other answer basically did. – Jesper.Reenberg Jun 11 '21 at 18:50
  • When a course is teaching a language, it usually starts out with fewer examples with limited commands, and pedagogy slowly adds to and modifies the know commands. Hence, a request to not go ahead and use a feature that has not been discussed is reasonable. Most of everything posted in response to my question is wrong is this regard; no, thinking iteratively is not my problem and curt responses useless. Follow the parameters I lay out and answer the question as stated or ask a clarifying question. – bentaisan Jul 05 '21 at 06:45
3
fun number_in_month (L : (int*int*int) list, m : int) =
if L = nil
then 0
else
    (if #2 (hd L) = m then 1 else 0) + number_in_month (tl L,m);

TESTING:

number_in_month ([] , 2);
number_in_month ([(1,2,3)] , 2);
number_in_month ([(1,2,3),(2,2,2)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,28,19)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,2,19)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,28,19)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,28,19)] , 2);
number_in_month ([(1,2,3),(2,2,2),(19,11,29),(10,28,19),(16,2,7)] , 2);

Reference:

http://www.cs.sunysb.edu/~leo/CSE215/smllistexamples.txt

http://www.standardml.org/Basis/list.html

New Coder
  • 499
  • 4
  • 22
  • Part of the problem is that week by week, new features of SML are introduced and there are often restrictions on using more advanced features. Perhaps for these types of questions, the constraints on features should be stated. Alternately, perhaps the “correct” technique is demonstrated as well as the constrained example, which would allow for a mapping of concepts. – bentaisan Oct 09 '20 at 18:08