Search code examples
listinitializationtuplesocaml

How should I initialize my list of tuples


I have a function that takes a list of tuples and process it to obtain a tuple of 3 integers. I would now like to test it with a list of the tuples type I created but I'm unable to create this list.

Here is my tuple type :

    type t_votes = {valeur : string ; nombre : int };;

Here is my function :

let rec recap (l : t_votes list) : int * int * int =
    let (nb_oui,nb_non,nb_blanc) = recap(tl(l)) in
    if (l=[]) then
       (0,0,0)
    else if ((hd(l)).valeur = "oui") then
       (nb_oui+(hd(l)).nombre ,nb_non,nb_blanc)
    else if ((hd(l)).valeur = "non") then
       (nb_oui, nb_non + (hd(l)).nombre, nb_blanc)
    else if ((hd(l)).valeur = "blanc") then
       (nb_oui,nb_non,nb_blanc+(hd(l)).nombre)
    else
       failwith("liste invalide")
;;

And here is my vain attempt at declaring a list to test my function with :

let liste_votes : t_votes list = [("oui",120);("non",18);("blanc",20);("oui",20);("non",24);("blanc",25)];;
recap(liste_votes );;

Here is what tuareg gives me :

# let liste_votes : t_votes list = [("oui",120);("non",18);("blanc",20);("oui",20);("non",24);("blanc",25)];;
Characters 34-45:
  let liste_votes : t_votes list = [("oui",120);("non",18);("blanc",20);("oui",20);("non",24);("blanc",25)];;
                                    ^^^^^^^^^^^
Error: This expression has type 'a * 'b
       but an expression was expected of type t_votes

Solution

  • To create a value of a record type (because it is a record type and not a tuple, a tuple doesn't name its arguments), the syntax is the following:

    { valeur = "Some string" ; nombre = 13 }
    

    If this syntax is too heavy for you, a common practice is to write a builder function:

    let mk_vote valeur nombre = { valeur ; nombre }
    

    Here I'm using another piece of syntax to instantiate a record value without using the = symbol. In that case, it's the same as writing valeur = valeur and nombre = nombre.

    You can then write:

    let votes = [ mk_vote "oui" 120 ; mk_vote "non" 18 ; mk_vote "blanc" 20 ; mk_vote "oui" 20 ; mk_vote "non" 24 ; mk_vote "blanc" 25 ]
    
    let mk_vote (valeur, nombre) = { valeur ; nombre }
    

    would work as well and let you write

    let votes = List.map mk_vote [("oui",120);("non",18);("blanc",20);("oui",20);("non",24);("blanc",25)]
    

    For some vote of the record type you can access the fields with vote.valeur and vote.nombre. You can also use pattern-matching:

    match vote with
    | { valeur = v ; nombre = n } => (* ... *)
    

    You can also make a record value from another one like so:

    let vote = { valeur = "Some string" ; nombre = 13 } in
    let vote' = { vote with valeur = "Some other string" } in
    (* ... *)
    

    Then vote'.valeur is "Some other string" while vote'.nombre is vote.nombre, or 13 in that case.


    Finally I couldn't help but notice you were using strings to represent different kind of votes, since there seem to be only three cases, a dedicated type would be more relevant (you are using ocaml after all which lets you handle data properly).

    type vote_kind =
    | Yes
    | No
    | Blank
    
    type t_votes = {
      value : vote_kind ;
      amount : int ;
    }