Вы можете собрать эту функцию из частей, которые либо являются стандартными, либо должны быть такими. В принятом ответе есть правильное представление о молниях. Мой ответ о дифференцировании и комонадах дает общее описание соответствующих операций, но позвольте мне быть конкретным здесь.
Я определяю тип «списков с одним отверстием элемента» следующим образом:
data Bwd x = B0 | Bwd x :< x deriving Show
type HoleyList x = (Bwd x, [x])
Строго говоря, мне не нужно вводить обратные списки, чтобы сделать это, но я так легко запутаюсь, если мне нужно перевернуть что-то в своей голове. (Так получилось, что HoleyList
является формальной производной от []
.)
Теперь я могу определить, что значит быть элементом списка в его контексте.
type InContext x = (HoleyList x, x)
Идея состоит в том, что второй компонент пары находится между обратным списком и прямым списком. Я могу определить функцию, которая снова соединяет список (называется upF
в общей обработке).
plug :: InContext x -> [x]
plug ((B0, xs), y) = y : xs
plug ((xz :< x, xs), y) = plug ((xz, y : xs), x)
Я также могу определить функцию, которая дает все способы разобрать список на части (downF
, в общем случае).
selections :: [x] -> [InContext x]
selections = go B0 where
go xz [] = []
go xz (x : xs) = ((xz, xs), x) : go (xz :< x) xs
Обратите внимание, что
map snd (selections xs) = xs
map plug (selections xs) = map (const xs) xs
И теперь мы можем следовать рецепту Бартека.
selectModify :: (a -> Bool) -> (a -> a) -> [a] -> [[a]]
selectModify p f = map (plug . (id *** f)) . filter (p . snd) . selections
То есть: отфильтруйте выборки по тесту, примените функцию к элементу в фокусе, затем соедините обратно. Если у вас есть застежка-молния, то это однострочник, и он должен работать для любого дифференцируемого функтора, а не только для списков! Работа выполнена!
> selectModify ((1 ==) . (`mod` 2)) (2*) [1..10]
[[2,2,3,4,5,6,7,8,9,10]
,[1,2,6,4,5,6,7,8,9,10]
,[1,2,3,4,10,6,7,8,9,10]
,[1,2,3,4,5,6,14,8,9,10]
,[1,2,3,4,5,6,7,8,18,10]]
person
pigworker
schedule
25.09.2014