0
votes

I have a method that returns the count of the most common element in a list (specifically a list of char values)

Here's my code:

let getMostFrequentCharCount li =
    let hashTable = Hashtbl.create (List.length li)
    in
    let rec getMostFrequentCharCount li acc =
        match li with
            [] -> acc
            | head::tail ->
                (match (Hashtbl.find hashTable head) with
                    exception Not_found -> Hashtbl.add hashTable head 1
                    | _ -> let currentFreq = Hashtbl.find hashTable head 
                           in
                           Hashtbl.replace hashTable head (currentFreq+1));
                let currentFreq = Hashtbl.find hashTable head
                in
                if currentFreq > acc
                then
                    getMostFrequentCharCount tail currentFreq
                else
                    getMostFrequentCharCount tail acc
    in
    getMostFrequentCharCount li 0;;

For some reason, when I remove the parentheses surrounding the second pattern matching block (starts with match (Hashtbl.find hashTable head) with), my compiler complains that my accumulator acc has type unit in the next if-statement if currentFreq > acc when acc should have type int.

Why are the parentheses fixing this error?

1

1 Answers

2
votes

In OCaml syntax, a match branch (after ->) contains a sequence of expressions separated by ;. Therefore without the parentheses, the following lines are parsed as part of the match branch for _.

Since Hashtbl.add returns unit, the first branch of this match is of type unit. This means the _ branch must also be of type unit. When you don't have the parentheses, the following lines cause a type error because they're not of type unit.

I used ocamlformat to format the outer match with and without the parentheses.

Here is the formatted code with parentheses:

match li with
| [] -> acc
| head :: tail ->
    ( match Hashtbl.find hashTable head with
    | exception Not_found -> Hashtbl.add hashTable head 1
    | _ ->
        let currentFreq = Hashtbl.find hashTable head in
        Hashtbl.replace hashTable head (currentFreq + 1) );
    let currentFreq = Hashtbl.find hashTable head in
    if currentFreq > acc then getMostFrequentCharCount tail currentFreq
    else getMostFrequentCharCount tail acc

Here is the formatted code without parentheses:

match li with
| [] -> acc
| head :: tail -> (
    match Hashtbl.find hashTable head with
    | exception Not_found -> Hashtbl.add hashTable head 1
    | _ ->
        let currentFreq = Hashtbl.find hashTable head in
        Hashtbl.replace hashTable head (currentFreq + 1);
        let currentFreq = Hashtbl.find hashTable head in
        if currentFreq > acc then getMostFrequentCharCount tail currentFreq
        else getMostFrequentCharCount tail acc )

I think this does a pretty good job of illustrating the problem.