Search code examples
sml

SML: Create a list from a list of tuples with the second element of each tuple


Hello I'm new at SML and I've been trying to write a function that gets as a parameter a list (in my case the list prussia) which has tuples with two ints and a string, my function has to create a list with all the years that appear in the list without repetitions (2nd element of each tuple of the list). I have to do it creating two functions (append_if_new takes a year of the list and adds it in the list, it works) and year has to do it for all the tuples in the list, i tried it using foldl but i get a tycon mismatch.

Pd. to do it i have to use the function map, filter or fold and i can move append_if_new functionalities to the year function. I think the error is in the fold call where the function i pass as parameter is not the type of function i should pass but I'm not sure what is the problem. Thanks

    val prussia =
  [(0,1875,"G"),(2,1876,"G"),(2,1877,"G"),(1,1878,"G"),(0,1879,"G"),
   (0,1880,"G"),(1,1881,"G"),(1,1882,"G"),(0,1883,"G"),(3,1884,"G"),
   (0,1885,"G"),(2,1886,"G"),...] : (int * int * string) list

fun append_if_new (lista:(int*int*string)list): int list =
    let 
        val lista2 = []
        val x = hd lista
        val z = #2x
    in
        if (List.exists (fn y => y = z) lista2) 
        then lista2
        else lista2@[z]
    end

fun years (lista:(int*int*string)list): int list =
    List.foldl append_if_new 0 lista

Solution

  • create a list with all the years that appear in the list without repetitions

    (2nd element of each tuple of the list)

    You can create a list with repetitions using map and filter duplicates afterwards:

    fun year_of (_, year, _) = year
    fun member (y, xs) = List.exists (fn x => y = x) xs
    fun nub [] = []
      | nub (x::xs) = if member (x, xs)
                      then nub xs
                      else x :: nub xs
    
    fun years records = nub (map year_of records)
    

    Here nub has an asymptotic running-time complexity of O(n²), which is bad and unnecessary. You can also go straight ahead and fold the list such that you never insert the duplicates to begin with:

    fun member (y, xs) = List.exists (fn x => y = x) xs
    
    fun years records =
        let
          fun insert ((_, year, _), years) =
              if member (year, years)
              then years
              else year :: years
        in
          foldr insert [] records
        end
    

    but the asymptotic running time is the same, and it is slightly more obscure to read. If you want to filter duplicates in an efficient way, you have to use a more efficient data structure to manage duplicates, like a tree-based set or similar. In Haskell, this is the difference between nub and nubOrd.