#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, когда оператор ifif currentFreq > acc
не является оператором return соответствующейmatch
ветви?2. Рассмотрим вторую версию выше без круглых скобок. Все ветви a
match
должны (конечно) иметь один и тот же тип. Поэтомуacc
должен быть любой тип вложенногоmatch
. Первая вложенная ветвьmatch
явно имеет тип unit . Поэтомуacc
должна иметь тип unit. Поскольку она сравнивается сcurrentFreq
incurrentFreq > 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. Это то, что вы имели в виду?