Подтвердить что ты не робот

Learning Haskell: Как удалить элемент из списка в Haskell

Попытка научиться Haskell. Я пытаюсь написать простую функцию для удаления числа из списка без использования встроенной функции (удалить... я думаю). Для простоты предположим, что входной параметр является целым, а список - целым. Вот код, который у меня есть, Пожалуйста, скажите мне, что случилось со следующим кодом.

areTheySame :: Int -> Int-> [Int]

areTheySame x y | x == y = []
                | otherwise = [y]

removeItem :: Int -> [Int] -> [Int]

removeItem x (y:ys) = areTheySame x y : removeItem x ys
4b9b3361

Ответ 1

Другие правы, что проблема - это оператор :. Я бы сказал, что ваша функция areTheySame, которая возвращает список, в любом случае является неправильным подходом. Вместо того, чтобы переключиться на оператор ++, лучше реализовать эту функцию:

removeItem _ []                 = []
removeItem x (y:ys) | x == y    = removeItem x ys
                    | otherwise = y : removeItem x ys

Как вы можете видеть, это довольно простая реализация. Кроме того, такой подход гораздо менее облагается налогом для вашей программы, чем добавление группы списков. Он также имеет другие преимущества, такие как работа лениво.

Ответ 2

Оператор : не выполняет то, что вы думаете:

(:) :: a -> [a] -> [a]

Требуется элемент типа a и добавляет его в начало списка типа a. Вы используете его для объединения двух списков типа a. Для этого вам нужно использовать ++:

(++) :: [a] -> [a] -> [a]

Кроме того, если вы создаете рекурсивную функцию, ей требуется условие окончания. Поэтому попробуйте следующее:

removeItem _ [] = []
removeItem x (y:ys) = areTheySame x y ++ removeItem x ys

Таким образом, когда вы дойдете до конца списка, функция перестанет рекурсивно.

Ответ 3

Вы также можете сделать это как понимание списка

delete :: Eq a => a -> [a] -> [a]
delete deleted xs = [ x | x <- xs, x /= deleted ]

Ответ 4

Это минимальное исправление, чтобы ваш пример работал:

removeItem :: Int -> [Int] -> [Int]
removeItem _ []     = []
removeItem x (y:ys) = areTheySame x y ++ removeItem x ys

Во-первых, вам нужно использовать ++ для объединения списков, так как используемый вами оператор : добавляет только один элемент в начало списка (его нельзя использовать для добавления списков с одним элементом или для добавления пустые списки). Сначала вы сравните голову списка (y) с элементом, который хотите удалить, и правильно верните элемент или пустой список, используя areTheySame. Затем вы хотите рекурсивно продолжить использование removeItem в остальной части списка (ys). Полученный список необходимо объединить с помощью ++.

Во-вторых, как заметил Крис Лутц, вам нужно условие окончания, когда вы достигнете конца списка. Добавив эту строку, Haskell знает, что делать с пустым списком (то есть ничего, просто верните пустой список).

Как сказал Чак, вы можете упростить код для этой задачи, если removeItem не делегирует задачу сравнения, но сравните себя и выбросьте элемент, если он должен быть удален, в противном случае сохраните его в заголовке списка (используя :). В любом случае, продолжайте рекурсивно с остальной частью списка.

-- nothing can be removed from an empty list
-- ==> return empty list and stop recursion
removeItem _ []     = []

-- if the list is not empty, cut off the head in y and keep the rest in ys
--                    if x==y, remove y and continue
removeItem x (y:ys) | x == y    = removeItem x ys    
--                    otherwise, add y back and continue 
                    | otherwise = y : removeItem x ys

Ответ 5

Для справки вам может быть интересно увидеть как это сделать в delete из Data.List.

Вы можете оставить areTheySame как есть, но тогда вам нужно будет использовать concatMap в removeItem, чтобы свернуть пустые списки:

removeItem :: Int -> [Int] -> [Int]
removeItem x xs = concatMap (areTheySame x) xs

или эквивалентно

removeItem :: Int -> [Int] -> [Int]
removeItem x = concatMap (areTheySame x)

Обратите внимание, что типы ваших функций могут быть более общими:

areTheySame :: (Eq a) => a -> a -> [a]
removeItem  :: (Eq a) => a -> [a] -> [a]

Это позволяет удалить элементы из списков любого типа, для которых определен ==, а не только Int.

Ответ 6

Я написал функцию только в одной строке кода:

remove element list = filter (\e -> e/=element) list

Например:

remove 5 [1..10]

[1,2,3,4,6,7,8,9,10]

remove 'b' ['a'..'f']

"ACDEF"

Ответ 7

Вы помещаете пустые списки в середине списка, что является ошибкой.

Вместо этого попробуйте что-то вроде

removeItem x (y:ys) = | x == y = removeItem x ys
                      | otherwise = y : removeItem x ys 
Забастовкa >

Ответ 8

Я считаю, что все решения, данные до сих пор, работают иначе, чем Data.List.delete, который удаляет только первый элемент.

deleteFromList x xs =
  case break (==x) xs of
    (_,[]) -> xs
    (notsat,sat) -> notsat ++ tail sat

была моя попытка удалить только первый член (еще не достигший максимума в D.L).

Непонятно, какое поведение хочет высший плакат.