Очевидно, что это очень специфическая проблема.Часто бывает полезно взглянуть на более широкую картину: какая более общая проблема это частный случай?Понятно, что здесь мы просматриваем список и можем увидеть элемент, который мы хотим заменить, ноль или более.Далее, мы хотим посмотреть, сколько способов можно сделать ограниченное количество таких замен.Итак, давайте реализуем общий случай, прежде чем думать о том, как специализироваться на нашей первоначальной задаче:
import Control.Applicative (Alternative, empty, (<|>))
replaceNTimes :: Alternative f => (a -> f a) -> Int -> [a] -> f [a]
replaceNTimes _ 0 xs = pure xs
replaceNTimes _ _ [] = empty
replaceNTimes f n (x:xs) = replaceHere <|> keepLooking
where replaceHere = (:) <$> f x <*> replaceNTimes f (n - 1) xs
keepLooking = (x:) <$> replaceNTimes f n xs
Если у нас есть «бюджет» из оставшихся нулевых замен, мы просто возвращаем оставшуюся часть списка.Если у нас остался бюджет, но список пуст, мы отменяем, потому что мы не смогли сделать ожидаемое количество замен.В противном случае, мы обращаемся к нашей функции подсказки замены, чтобы увидеть, какие замены являются законными в текущей позиции, и выбираем либо сделать одну из них и выполнить рекурсию с меньшим N, либо сделать таковую и рекурсировать с тем же N.
С этим инструментом в нашем распоряжении, исходная проблема проста: мы просто специализируем N на 1 (производим ровно одну замену) и предоставляем заменяющую функцию, которая предлагает только замену -1 на 0:
replaceSingleNegativeOneWithZero :: [Int] -> [[Int]]
replaceSingleNegativeOneWithZero = replaceNTimes go 1
where go (-1) = [0]
go _ = []
И проверьте, чтобы мы получили ожидаемый результат:
*Main> replaceSingleNegativeOneWithZero [-1,0,0,1,-1,-1,1,1,0]
[ [0,0,0,1,-1,-1,1,1,0]
, [-1,0,0,1,0,-1,1,1,0]
, [-1,0,0,1,-1,0,1,1,0]]