Скрытие переменной в сопоставлении шаблонов OCaml

#ocaml

#ocaml

Вопрос:

У меня есть метод, который возвращает количество наиболее распространенных элементов в списке (в частности, список значений символов)

Вот мой код:

 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;;
  

По какой-то причине, когда я удаляю круглые скобки, окружающие второй блок сопоставления с образцом (начинается с match (Hashtbl.find hashTable head) with ), мой компилятор жалуется, что мой аккумулятор acc имеет тип unit в следующем if-операторе if currentFreq > acc , когда acc должен иметь тип int .

Почему скобки исправляют эту ошибку?

Ответ №1:

В синтаксисе OCaml match ветвь (после -> ) содержит последовательность выражений, разделенных ; символом . Поэтому без круглых скобок следующие строки анализируются как часть match ветви for _ .

Поскольку Hashtbl.add возвращает единицу, первая ветвь этого соответствия имеет тип unit . Это означает _ , что ветвь также должна иметь тип unit. Если у вас нет круглых скобок, следующие строки вызывают ошибку типа, поскольку они не относятся к типу unit .

Я использовал ocamlformat для форматирования внешнего match с помощью и без круглых скобок.

Вот форматированный код в круглых скобках:

 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
  

Вот форматированный код без круглых скобок:

 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 )
  

Я думаю, что это довольно хорошо иллюстрирует проблему.

Комментарии:

1. Спасибо за ваше объяснение; это очень полезно! Теперь мне интересно, почему acc предполагается, что она имеет тип unit, когда оператор if if currentFreq > acc не является оператором return соответствующей match ветви?

2. Рассмотрим вторую версию выше без круглых скобок. Все ветви a match должны (конечно) иметь один и тот же тип. Поэтому acc должен быть любой тип вложенного match . Первая вложенная ветвь match явно имеет тип unit . Поэтому acc должна иметь тип unit. Поскольку она сравнивается с currentFreq in currentFreq > acc , она также должна иметь тип int. Это ошибка типа. Сначала анализ определил тип единицы acc измерения, так что именно так формулируется сообщение.

3. Но если match ветвь определена как последовательность (следующая за -> ) из 1 или более выражений, разделенных ; , и указанная последовательность должна возвращать тот же тип, что и все остальные ветви в match блоке, почему acc (в этом выражении if-statement) считается возвращаемым значением (для сопоставления ветвей _ ), которое необходимоиметь тип unit, когда возвращаемое значение равно или getMostFrequentCharCount tail currentFreq getMostFrequentCharCount tail acc

4. Здесь в комментариях невозможно провести сложное обсуждение. Я могу добавить больше к ответу, если хотите. Но чтобы ответить быстро: анализ должен работать в определенном порядке. Есть три вещи, которые должны быть одного типа: acc , Hashtbl.add , и выражение, которое вы упомянули. Анализ может продолжаться, предполагая, что первые два имеют один и тот же тип. Т.Е. Он может начаться с предположения, что acc это тип unit . Если код введен правильно, порядок не имеет значения. В противном случае вы можете получить разные ошибки в зависимости от порядка выполнения анализа.

5.О, подождите, я думаю, я понял. Без круглых скобок match ветвь, соответствующая exception Not_found возвращает тип единицы, поэтому компилятор предполагает, что внешняя match ветвь (соответствующая [] ) также возвращает тип единицы, который хранится в переменной acc . Итак, теперь, когда компилятор предположил acc , что это тип unit на данный момент, компилятор запутывается, когда acc находится в if-инструкции, когда acc не является типом int. Это то, что вы имели в виду?