Изучение 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
21
задан Don Stewart 18 April 2011 в 11:04
поделиться

6 ответов

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

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

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

28
ответ дан 16 October 2019 в 23:44
поделиться

для справки, вам может быть заинтересован в виде просмотра , как это сделано в Удалить из данных. Список .

Вы могли бы уйти Aretheysame Aretheysame , как есть, но вам нужно использовать CONCATMAP в RESHINGITEM , чтобы свернуть пустые списки:

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 Отказ

3
ответ дан 16 October 2019 в 23:44
поделиться

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

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

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

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

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

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

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

10
ответ дан 16 October 2019 в 23:44
поделиться

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

delete :: Eq a => a -> [a] -> [a]
delete deleted xs = [ x | x <- xs, x /= deleted ]
9
ответ дан 16 October 2019 в 23:44
поделиться

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

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

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

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

Как сказал Чак, вы можете упростить код для этой задачи, если 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
3
ответ дан 16 October 2019 в 23:44
поделиться

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

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

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

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

0
ответ дан 16 October 2019 в 23:44
поделиться
Другие вопросы по тегам:

Похожие вопросы: