Я бы начал с форматирования вашего решения следующим образом:
fun lookSay [] = []
| lookSay (x::xs) =
let
fun helper(y, z::zs, count) =
if y = z
then helper(y, zs, count + 1)
else (count, y) :: lookSay zs
in
helper(x, xs, 1)
end
Я использовал x
, y
и z
вместо l
и l'
, потому что я обнаружите, что l
может быть трудно читать: я не могу легко увидеть, является ли это строчными буквами L, прописными буквами i или 1. Поскольку мы на самом деле также используем константу 1, это добавляет путаницу.
Я также сделал несколько переименований переменных: у вас есть одна пара l
и ls
в поле case-of, и другая пара с таким же именем пара l
и ls
внутри вспомогательной функции, которая скрывает внешние переменные. Хотя это работает, это очень запутанно, потому что разные вещи в непосредственной близости называются одинаковыми.
Подобным образом я переименовал acc
в count
, чтобы уточнить, что он накапливает.
Как намекнул molbdnilo Ваша проблема заключается в отсутствующем сопоставлении с образцом. Компилятор ML должен предупредить вас об этом:
! Toplevel input:
! ..........helper(y, z::zs, count) =
! if y = z
! then helper(y, zs, count + 1)
! else (count, y) :: lookSay zs
! Warning: pattern matching is not exhaustive
Что намекает на то, что helper
также должен иметь шаблон []
.
Что касается стратегии этого решения: Внутренний * Функция 1031 * делает две вещи, которые требуют внутренней вспомогательной функции. Во-первых, у него есть дополнительный аргумент для «текущего» элемента, который я назвал y
, а во-вторых, у него есть накопительный аргумент, который я назвал count
. В частности, helper
вызывает lookSay
, а не себя.
Но только второй из них действительно необходим, поскольку вы также можете использовать заголовок аргумента списка, чтобы сохранить «текущий» элемент:
fun lookSay xs =
let
fun go [] _ = []
| go (x::y::zs) count =
if x = y
then go (x::zs) (count + 1)
else (count + 1, x) :: go (y::zs) 0
in
go xs 0
end
Эта стратегия просматривает сразу два первых элемента списка, x
и y
, и, если они равны, отбрасывает один, но увеличивает count
. Если x
и y
не равны, это означает конец последовательности x
с, поэтому элемент (count + 1, x)
испускается и go
может рекурсивно вызывать себя для y::zs
.
Я сохранил ошибку, которая также присутствует в вашем примере. В моей версии подсказка molbdnilo может быть перефразирована как «Подумайте о том, как функция go
обрабатывает списки с одним элементом».
Я думаю, что для того, чтобы сделать эту функцию немного более полезной, вы хотите на самом деле выдайте простой список:
fun concatMap f xs =
List.concat (List.map f xs)
fun flatten pairs =
concatMap (fn (n, x) => [n, x]) pairs
fun lookSay xs =
let
fun go [] _ = []
| go (x::y::zs) i =
if x = y
then go (x::zs) (i+1)
else (i+1,x) :: go (y::zs) 0
in flatten (go xs 0) end
Если предположить, что ошибка исправлена, это должно дать вам:
- lookSay [1, 1, 1, 2, 2, 3];
> val it = [3, 1, 2, 2, 1, 3] : int list