简体   繁体   English

将函数映射到列表中的链式成对元素上

[英]Mapping a function on chained pairs of elements from a list

I want to map a function on chained pairs of elements from a list. 我想将函数映射到列表中的链式成对元素上。 Consider the list: 考虑以下列表:

a = [1,2,3,4,5]

By using eg the function f a1 a2 = a1 < a2 , I want to get: 通过使用例如函数f a1 a2 = a1 < a2 ,我想要得到:

[True,True,True,True]

The following seems to work in general: 以下内容通常可以正常工作:

zipWith f a (tail a)

However, I find it kind of hackish. 但是,我发现它有点骇人听闻。 Is there a more appropriate way, maybe using fold? 有没有更合适的方法,也许使用折叠?

I think I can speak on behalf of the Haskell community if I tell you that that is the most idiomatic way of doing this in Haskell. 我想我可以代表Haskell社区发言,如果我告诉你,这是Haskell中最惯用的方式 You could hack something together using a fold but it is clearly not as readable as the first variant: 您可以使用折叠将某物砍在一起,但显然不如第一个变体可读:

f p xs = snd $ foldl (\acc x -> (x, snd acc ++ [p (fst acc) x])) (head xs, []) (tail xs)

Thank you @WillNess for supplying a more readable version of above's function, although it still does not beat zipWith in brevity and clarity. 感谢@WillNess提供了上述功能的更具可读性的版本,尽管它在简洁性和清晰度方面仍然没有击败zipWith

f p = foldr g [] . tails where g (x:y:_) r = p x y:r; g _ _ = []

This is really hackish. 这真是骇人听闻的。 The problem with using a fold in this situation is that normally the elements will be dealt with separately and they do not "know" about their neighbours. 在这种情况下使用折叠的问题在于,通常将单独处理元素,并且它们不会“知道”其邻居。 The only values they know are the accumulator and their own. 他们知道的唯一值是累加器和它们自己的值。

In this example, doing f (<) [1,2,3,4,5] will check if the left element of a pair is smaller than the one before, but it is way less readable than just doing: 在这个例子中,做f (<) [1,2,3,4,5]将检查是否有对左侧元件是比前一个小,但它比只是做方式以下可读:

f p xs = zipWith p xs (tail xs)

Usage would be the same as above. 用法与上面相同。


Note that I am no professional in the Haskell language, so it may be that this approach using a fold could have been written in a better way, but nevertheless it will never beat the elegance of the zipWith approach. 请注意,我不是Haskell语言的专业人士,因此可能使用一种更好的方式使用折痕编写了这种方法,但是它永远都不会击败zipWith方法的优雅之zipWith

You can use the Traversable instance over a State functor to achieve the expected result, like so : 您可以在State函数上使用Traversable实例来达到预期的结果,如下所示:

traverseSt :: Traversable t => (a -> a -> b) -> a -> t a -> t b
traverseSt f x ta = runState (traverse (\a -> get >>= \a' -> f a a'<$put a) ta) x

You may then specialize this function to work on non-empty lists (since you need to know at least one element to provide the first seed value), which looks like : 然后,您可以专门使用此函数来处理非空列表(因为您需要知道至少一个元素来提供第一个种子值),如下所示:

zipSelf :: (a -> a -> b) -> [a] -> [b]
zipSelf f (a:as) = traverseSt f a as
zipSelf f [] = error "Impossible use of zipSelf on empty structure"

Splitting code in this way, you may also define zipSelf over other structures, such as Tree s or Maybe s, or any type for which a first element is easily extractible. 通过这种方式拆分代码,您还可以在其他结构上定义zipSelf ,例如TreeMaybe ,或者可以轻松提取第一个元素的任何类型。

Cheerio, 谢里奥

You could write a function like 你可以写一个像

movingApply :: (a -> a -> b) -> [a] -> [b]
movingApply f (x:y:xs) = f x y : movingApply f (y:xs)
movingApply f _ = []

(feel free to pick a better name, this one is terrible), but this is computationally the same as zipWith fx (drop 1 x) when you expand it out. (随意选择一个更好的名称,这个名称很糟糕),但是在扩展时,它在计算上与zipWith fx (drop 1 x)相同。 Note that I've used drop 1 instead of tail , this is because tail is a partial function, and tail [] results in a runtime error while drop 1 [] returns [] . 请注意,我使用drop 1而不是tail ,这是因为tail是部分函数,​​而tail []导致运行时错误,而drop 1 []返回[] Since this is pretty much the same thing as the zipWith variety, I'd prefer zipWith since it's a one-liner and I don't have to do the recursion manually. 由于这几乎与zipWith变体相同,因此我更喜欢zipWith因为它是单行的,而且我不必手动进行递归。

You could use mapAccumL 您可以使用mapAccumL

import Data.List
a = [1,2,3,4,5]
compareAdjacent = snd $ mapAccumL (\acc x -> (x,(x > acc))) 0 a

This produces the output [True,True,True,True,True]. 这将产生输出[True,True,True,True,True]。 If you want it to ignore the first list item, you could create a function like this: 如果希望它忽略第一个列表项,则可以创建如下函数:

compareAdjacent' (x:xs) = snd $ mapAccumL (\acc x -> (x,(x > acc))) x xs
compareAdjacent' [] = []

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

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