简体   繁体   中英

Missing Haskell primitive to apply a function to each element of a list successively?

In Haskell, it is well known that the map primitive can be used to apply a given function to all elements of a list:

 λ> map toUpper "abcd"
"ABCD"
 λ> 

While trying to generate all partitions of a finite set (list), the following, similar primitive would be handy:

 λ> sap toUpper "abcd"
["Abcd","aBcd","abCd","abcD"]
 λ> 

with sap standing for successive applications . The type signature would be:

sap :: (a -> a) -> [a] -> [[a]]

For example, part of the partitions of set "abcd" can be obtained from the partitions of "bcd" by sap'ing them with ('a':).

 λ> pbcd = [["b","c","d"],["b","cd"],["bc","d"],["c","bd"],["bcd"]]
 λ> 
 λ> concatMap (sap ('a':)) pbcd
[["ab","c","d"],["b","ac","d"],["b","c","ad"],["ab","cd"],["b","acd"],["abc","d"],["bc","ad"],["ac","bd"],["c","abd"],["abcd"]]
 λ> 

and the 5 missing partitions can then be obtained by adding 'a' as its own separate singleton.

My problem is that I have been unable to locate such a primitive in the language libraries, and that Hoogle , given the type signature, returns nothing of interest.

Does such a primitive as sap exist somewhere in the Haskell language libraries ??? Or is there a way to write it that is so short and simple that it does not even deserve to be a separate function, putting it below the so-called Fairbairn threshold ?

Footnote : It is possible to write sap like this:

sap :: (a -> a) -> [a] -> [[a]]
sap fn ls = fst  $  foldr  op  ([], [])  ls
   where  op x (ll,tl) = ( ((fn x):tl) : map (x:) ll , x:tl )

Essentially you start with [[fn (last ls)]] as a seed and then progress leftwards. But this seems pedestrian not simple.

It seems like the simplest version of this is direct recursion:

sap :: (a -> a) -> [a] -> [[a]]
sap _ [] = []
sap f (x:xs) = (f x : xs) : map (x:) (sap f xs)

One possible exploration of this is as a paramorphism , which gives access to the recursive result and the unprocessed remainder together.

sap f = para step where
    step Nil = []
    step (Cons x (xs, rest)) = (f x : xs) : map (x:) rest

(Not checked, might have silly errors)

I don't see that as a huge improvement though. I don't see any deep insights in that decomposition of recursion from the problem itself.

For that, well... I've used holesOf for a generalized version of this in the past.

sap :: Traversable t => (a -> a) -> t a -> [t a]
sap f = map (peeks f) . holesOf traverse

Now that definitely says something . It has generalized the type to work on all instances of Traversable . On the other hand, the theoretical chunks involved were so overpowered for the end result that I'm not sure what it actually is that it says. On the third(?) hand, it looks pretty.

Or is there a way to write it that is so short and simple that it does not even deserve to be a separate function, putting it below the so-called Fairbairn threshold?

This. The functionality is rarely needed, and the (a -> a) argument doesn't make for a very generic application.

A short and simple implementation can be achieved with list recursion:

sap :: (a -> a) -> [a] -> [[a]]
sap _ []     = []
sap f (x:xs) = (f x:xs):((x:) <$> sap f xs)

I don't think it exists anywhere, although proving it negatively is of course impossible.. Another way to write sap , which I would probably prefer over using foldr ,

sap f ls = zipWith (alterWith f) [0..] (iterate ls)
  where alterWith f i ls = take i ls ++ f (ls !! i) : drop (i+1) ls

alterWith is available as adjust in https://hackage.haskell.org/package/fft-0.1.8.6/docs/Math-FFT-Base.html#v:adjust , but I would very much not bring something so heavyweight in for that function. I often have something like alterWith defined in a project already, though, and if so that allows sap to be elided in favor of the call to zipWith above.

Exploiting Data.List.HT.splitEverywhere :

import Data.List.HT

sap :: (a -> a) -> [a] -> [[a]]
sap f xs = [ pre ++ f x : post | (pre,x,post) <- splitEverywhere xs]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM