Удалите первое вхождение данного элемента в списке

#list #haskell

Вопрос:

Мне нужно создать код, который удаляет первое вхождение данного элемента в списке. До сих пор мне удавалось только заставить код удалять каждое вхождение данного элемента.

Теперь она мой кодовый ригт:

 rem2 :: Eq a => [a] -> a -> [a]
rem2 xs y = [x | x <- xs, x /= y]
 

Если я попытаюсь:

 rem2 "hello" 'l'
 

Код вернет «heo», в то время как я хочу, чтобы он вернул «helo». Кто-нибудь может мне помочь?

Ответ №1:

Вы можете работать с рекурсией здесь. Если вы дошли до конца списка, то вы возвращаете пустой список (1); если вам предоставлен непустой список (x:xs) и заголовок списка x соответствует элементу, который мы ищем (2), мы возвращаем конец списка xs ; и в случае, если элемент не соответствует (3), мы возвращаем x и повторяем в конце списка xs .

Таким образом, эта функция выглядит следующим образом:

 rem1 :: Eq a => [a] -> a -> [a]
rem1 ls y = go ls
    where go [] = …                  -- (1)
          go (x:xs) | … = …          -- (2)
                    | otherwise = …  -- (3) 

Я оставляю реализацию частей в качестве упражнения.

Ответ №2:

Вот упрощенное определение delete :

 delete :: a -> [a] -> [a]
delete  _ []    = []
delete x (y:ys) = if x == y then ys else y : delete x ys
 

Он использует более общую функцию deleteBy в реальной библиотеке. Вот исходный код.

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

1. Спасибо, именно то, что я хотел. Кстати, знаете ли вы, как может выглядеть код с использованием понимания списка?

2. понимание списка @eriksen1110 применяет одну и ту же функцию ко всем элементам списка. Для этой проблемы вы хотите удалить только первое вхождение, поэтому здесь вы не можете использовать понимание списка.

Ответ №3:

Нашел возможное решение, но не совсем то, что я хотел. Душа следующая:

 import Data.List

rem1 :: Eq a => [a] -> a -> [a]
rem1 xs y = delete y xs
 

Поэтому, если у кого-нибудь есть решение, которое не включает встроенную функцию, я с удовольствием выслушаю его от вас.

Ответ №4:

Вот любопытное решение, использующее понимание списка, как вы хотели:

 remfst :: Eq a => a -> [a] -> [a] 
remfst a xs 
   | (j:_) <- 
     [ i | (x,i) <- zip xs [0..], i <- [i | x == a]]
   = [ x | (x,i) <- zip xs [0..], x <- [x | i /= j]]
remfst _ xs = xs
 

Это «отвратительно»? В производственном коде, конечно. Здесь просто есть над чем поразмыслить.

Теперь у нас есть

 > remfst 'l' "helalo"
"healo"