CS54 - Fall 2022 - Class 5

Example code in this lecture

   more_list_recursion.sml
   uniquify.sml
   map_mystery.sml

Lecture notes

  • admin
       - assignment 2 due Sunday
       - Assignment submission
          - Run the assignment checker! It checks two things:
             1) that the function exists and
             2) that it has the correct type signature

  • look at the funPairs function in more_list_recursion.sml code
       - type signature?
          - 'a -> ('b * 'a) list -> 'b -> 'a
             - the 2nd parameter is a list of tuples by the pattern definition in the recursive case
             - u and x must be the same type because of u = x
             - the return type must be the same as v since we return it in the if statement
             - finally, since the function must always return the same type, we know that default (the first parameter), must be the same as v and the return type

       - what does the function do?
          - searches through the list of tuples (second argument) and tries to match x (the third argument) to the first entry of the tuple
          - if it finds one that matches, it returns the corresponding pair of the tuple
          - it not, it returns the default value
       - For example:
          > val grades = [("dave", 85), ("teran", 94), ("lisa", 92), ("chris", 83)];
          val grades = [("dave",85),("teran",94),("lisa",92),("chris",83)] : (string * int) list
          > funPairs 0 grades "dave";
          val it = 85 : int
          > funPairs 0 grades "chris";
          val it = 83 : int
          > funPairs 0 grades "carl";
          val it = 0 : int

       - What is the "_" in the first parameter?
          - It's still just a variable name
          - We use it to communicate that in that pattern, that parameter doesn't get used

  • what does the sumList function do?
       - sums up all of the values in a list of numbers (ints)

  • look at the gradeStatus function in more_list_recursion.sml code
       - Another example of the let expression
          - the syntax is:

             let
                declaration 1;
                declaration 2;
                ...
             in
                some_value
             end

             where the "declaration"s are either val or fun declaration.
          - Can serve multiple purposes:
             1) those declarations are only visible within the let expression, so it is a form of information hiding (like private in Java)
             2) allows you to break up computation into sequential steps when this is required
             3) can allow you to decompose values (e.g. to get at the values in a tuple, like in the 2nd declaration in the example)

       - ListPair.unzip is the opposite of ListPair.zip
          - ('a * 'b) list -> 'a list * 'b list

       - the function operates by
          1) use funPairs to extract the score for the student
          2) get the names as a list and the grades as a list so that we can
          3) calculate the average of all of the scores
          4) finally, the value returned is a string indicating whether the individual score is above or below the average

  • look at the member function in uniquify.sml code
       - What is its type signature?
          ''a -> ''a list -> bool

       - An aside, some times you will see sml use double quotes instead of single quotes for a type variable (i.e. ''a vs. 'a). There is a slight distinction, though we won't emphasize it in this class:
          - single quoted variable (e.g. 'a) indicates any general type
          - double quoted variable (e.g. ''a) indicates any type that supports equality
             - in the member function we are making an equality comparison, which is why it uses a double quoted type variable   

       - What does member do?
          - checks if the first argument occurs in the list
          - in the recursive call we check if "e=x". If this is true, then "true orelse <anything>" will be true
             - note SML will do short-circuited computation and will actually stop the recursion once it finds the element, though this doesn't affect the answer
          - only if we check all of the elements and we get to the base case do we then return false

  • look at the uniquify function in uniquify.sml code
       - What is its type signature?
          ''a list -> ''a list

       - What does uniquify do?
          - Takes in a list and returns the same list with all the duplicates removed
          - For each element it uses member to ask if that element is in the rest of the list
             - if it is, then it does not include that element in the answer and just recurses on xs
             - if it is not, then it does get included along with the recursion on the rest of the list

       - uniquify will keep the *last* occurrence of a repeated element because it will continue to skip it until it doesn't find it in the rest of the list
          - In particular
             uniquify [1, 2, 3, 1, 4, 2, 3]
       
             yields [1, 4, 2, 3] corresponding to the last occurrences of these

          - if we had kept the first occurrences it would have been [1, 2, 3, 4] (you'll have to code this up for the assignment :)

  • look at the mystery function in map_mystery.sml code
       - the way that we indicate that a parameter is a function is we use it like a function
          - (f x) implies a function call!

       - A few other examples
          fun temp f = (f 10) + 2;
          (int -> int) -> int

          - f is a function since we're calling it on the right
          - the input to the function is an int (10 in this case)
          - the output must also be an int since we're using it with "+ 2"

          fun temp2 myF = if myF "banana" then 1 else 2;
          (string -> bool) -> int

          - again, myF is a function
          - myF takes as input a string ("banana")
          - the return type for myF has to be a bool because we're using in the if clause

  • look at the mystery function in map_mystery.sml code
       - What is its type signature?
          ('a -> 'b) -> 'a list -> 'b list

          - Note that the first argument is a function because we're using it in the context of a function, i.e. (f x), a function call

       - What does it do?
          - For each element in the list, calls the function with that element and adds the result of that function call to the result.

       - For example:
          > fun negate x = ~x;
          val negate = fn : int -> int
          > negate 7;
          val it = ~7 : int
          > mystery negate [1, 2, 3, 4];
          val it = [~1,~2,~3,~4] : int list

  • map
       - the mystery function is so important (and common) that it is built into sml as a function call map
       - very powerful function
       - if you feel like you want to "loop" over a list, map can often be used instead of writing an additional recursive function.