Изменение элементов списка на основе индекса элемента


Используя Haskell:

Допустим, у меня есть список: [1,3,4,2,3] И я хочу изменить все 3 в списке. Я знаю, что могу применить это для выбора 3-х в этом случае:

map (\x -> if p x then f x else x) xs 
Однако функции, применяемые к тройкам, зависят от их индекса в списке. Так, например, если индекс был добавлен к нужному числу, то выход функции, которую я ищу, будет: [1,4,4,2,7].
2   2   2017-07-08 12:47:31

2 ответа:

Вы можете использовать zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] для этого:

zipWith (\i x -> if p x then f i x else x) [0..] xs

, где f, таким образом, принимает во внимание i (индекс) и x (элемент).

Например:

zipWith (\i x -> if x == 3 then (i+x) else x) [0..] xs

Который генерирует желаемый результат:

Prelude> let xs = [1,3,4,2,3]
Prelude> zipWith (\i x -> if x == 3 then (i+x) else x) [0..] xs
[1,4,4,2,7]

Вы можете инкапсулировать эту логику в отдельную функцию, например imap :: (Enum n, Num n) => (n -> a -> b) -> [a] -> b:

imap :: (Enum n, Num n) => (n -> a -> b) -> [a] -> b
imap = flip zipWith [0..]

Это будет работать с любым типом, который является экземпляром Num и Enum (так что Integer, Int, Float,...).

В то время как zipWith, вероятно, правильный путь, просто для разнообразия вы можете пойти рекурсивно следующим образом;

tpi :: [Int] -> [Int]
tpi = runner 0
      where runner _ []     = []
            runner n (x:xs) | x == 3    = (n + x) : runner (n+1) xs
                            | otherwise = x : runner (n+1) xs