Удаление хвостовых элементов в F#

#.net #f# #f#-interactive

#.net #f# #f # -интерактивный

Вопрос:

Я новичок в мире F #. У меня есть вопрос относительно удаления элементов в списке. Предположим, у вас есть произвольный список, скажем

 let list1 = [0;1;2;0;3;4;0;0]
  

Я хочу удалить все последние нули, чтобы список стал

list1 = [0;1;2;0;3;4]

Я попытался перевернуть список и проверить, равен ли head нулю, а затем выполнить рекурсивный вызов хвоста

 let rec remove ps = if List.rev(ps).Head <> 0 then ps else remove(ps.Tail);;
  

Однако я либо получаю код ошибки пустого списка, либо он просто возвращает исходный список.

Любая помощь приветствуется

Ответ №1:

Ваша «ошибка» заключается в следующем. Сначала вы переворачиваете список, поэтому:

 [0;0;4;3;0;2;1;0]
  

Затем вы проверяете, начинается ли он с 0 — это так. Затем вы передаете хвост исходного списка. Итак, вы удалили первое число, а не последнее:

 [1;2;0;3;4;0;0]
  

Поскольку вы никогда не удаляете последний ноль, он будет возвращаться true при тестировании 0 , пока вы не закончите вызов remove с пустым списком. Затем вы создаете исключение, пытаясь получить Head из пустого списка.

Это исправляет ошибку, при которой рекурсивный вызов с хвостом перевернутого списка (то есть без последнего номера) снова возвращается к исходному порядку:

 let rec remove ps =
    let reversed = List.rev ps
    if reversed.Head <> 0 then ps else remove (List.rev reversed.Tail)
  

Использование сопоставления с образцом может быть немного более идиоматичным. Это также не приведет к появлению пустого списка:

 let rec remove list =
    match List.rev list with
    | 0::xs -> remove (List.rev xs)
    | _ -> list
  

Вы также можете сделать то же самое без рекурсии. Переверните список один раз, пропускайте до тех пор, пока значение не 0 изменится, затем снова переверните его:

 let remove list =
    list
    |> List.rev
    |> List.skipWhile (fun x -> x = 0)
    |> List.rev
  

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

1. Большое вам спасибо и спасибо за объяснение, а не просто за чистый код.