简体   繁体   English

缺少 Haskell 原语以将函数连续应用于列表的每个元素?

[英]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:在 Haskell 中,众所周知, map原语可用于将给定函数应用于列表的所有元素:

 λ> 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 . sap代表连续应用 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':).例如,集合“abcd”的部分分区可以从“bcd”的分区中通过用('a':)sap'来获得。

 λ> 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.然后可以通过添加 'a' 作为它自己单独的单例来获得 5 个丢失的分区。

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.我的问题是,我一直无法在语言库中找到这样的原语,而且给定类型签名的Hoogle没有返回任何感兴趣的内容。

Does such a primitive as sap exist somewhere in the Haskell language libraries ??? Haskell 语言库中是否存在像sap这样的原语??? 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

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.基本上你从[[fn (last ls)]]作为种子开始,然后向左前进。 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.对此的一种可能的探索是作为paramorphism ,它可以一起访问递归结果和未处理的余数。

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.为此,嗯……我过去曾使用过holesOf作为通用版本。

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 .它已将类型概括为适用于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 -> a)参数不适用于非常通用的应用程序。

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 ,我可能更喜欢使用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. alterWith可在https://hackage.haskell.org/package/fft-0.1.8.6/docs/Math-FFT-Base.html#v:adjust 中作为adjust使用,但我不会带来如此重量级的东西那个功能。 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.不过,我经常已经在项目中定义了诸如alterWith东西,如果是这样,则可以省略sap以支持上面对 zipWith 的调用。

Exploiting Data.List.HT.splitEverywhere :利用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]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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