Haskell Is there a function for creating every variation of applying a function to a list

与世无争的帅哥 提交于 2021-02-16 18:54:05

问题


I want to create a list of variations of applying a function to every element of a list. Here is a quick example of what I mean.

applyVar f [a, b, c]
>> [[(f a), b, c], [a, (f b), c], [a, b, (f c)]]

Essentially It applies a function to each element of a list individually and stores each possible application in an array.

I'm not too sure how to approach a problem like this without using indexes as I have heard they are not very efficient. This is assuming that the function f returns the same type as the input list.

Is there a pre-existing function to get this behavior? If not what would that function be?


回答1:


To see if there's a pre-existing function, first figure out its type. In this case, it's (a -> a) -> [a] -> [[a]]. Searching for that type on Hoogle only returns a handful of matches, and by inspection, none of them do what you want.

To write it yourself, note that it operates on a list, and the best way to figure out how to write a function on a list is to define it inductively. This means you need to build two cases: one for an empty list, and one for a non-empty list that assumes you already know the answer for its tail:

applyVar f [] = _
applyVar f (x:xs) = _ -- use `applyVar f xs` somehow

Now we just need to fill in the two blanks. For the nil case, it's easy. For the cons case, note that the first sublist starts with f a, and the rest will all start with a. Then, note that the tails of the rest look an awful lot like the answer for the tail. From there, the pattern should become clear.

applyVar f [] = []
applyVar f (x:xs) = (f x:xs):map (x:) (applyVar f xs)

And here's a quick demo/test of it:

Prelude> applyVar (+10) [1,2,3]
[[11,2,3],[1,12,3],[1,2,13]]



回答2:


Note that, as is often the case, lens contains some tools that provide this as a special case of some far more abstract tooling.

$ cabal repl -b lens,adjunctions
Resolving dependencies...
GHCi, version 8.10.3: https://www.haskell.org/ghc/  :? for help
> import Control.Lens
> import Control.Comonad.Representable.Store

> let updateEach f = map (peeks f) . holesOf traverse
> :t updateEach
updateEach :: Traversable t => (s -> s) -> t s -> [t s]

> updateEach negate [1..3]
[[-1,2,3],[1,-2,3],[1,2,-3]]

> import qualified Data.Map as M
> updateEach (*3) (M.fromList [('a', 1), ('b', 2), ('c', 4)])
[fromList [('a',3),('b',2),('c',4)],fromList [('a',1),('b',6),('c',4)],fromList [('a',1),('b',2),('c',12)]]

This is honestly way overkill, unless you start needing some of the ways lens gets more compositional, like so:

> let updateEachOf l f = map (peeks f) . holesOf l
> updateEachOf (traverse . filtered even) negate [1..5]
[[1,-2,3,4,5],[1,2,3,-4,5]]

> updateEachOf (traverse . ix 2) negate [[1,2],[3,4,5],[6,7,8,9],[10]]
[[[1,2],[3,4,-5],[6,7,8,9],[10]],[[1,2],[3,4,5],[6,7,-8,9],[10]]]

But whether you ever end up needing it or not, it's cool to know that the tools exist.




回答3:


Yes. Two functions, inits and tails:

foo :: (a -> a) -> [a] -> [[a]]
foo f xs = [ a ++ [f x] ++ b  | a     <- inits xs 
                              | (x:b) <- tails xs]

(with ParallelListComp extension; equivalent to using zip over two applications of the two functions, to the same input argument, xs, in the regular list comprehension).

Trying it out:

> foo (100+) [1..5]
[[101,2,3,4,5],[1,102,3,4,5],[1,2,103,4,5],[1,2,3,104,5],[1,2,3,4,105]]


来源:https://stackoverflow.com/questions/66073694/haskell-is-there-a-function-for-creating-every-variation-of-applying-a-function

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!