繁体   English   中英

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

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

我想将函数映射到列表中的链式成对元素上。 考虑以下列表:

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

通过使用例如函数f a1 a2 = a1 < a2 ,我想要得到:

[True,True,True,True]

以下内容通常可以正常工作:

zipWith f a (tail a)

但是,我发现它有点骇人听闻。 有没有更合适的方法,也许使用折叠?

我想我可以代表Haskell社区发言,如果我告诉你,这是Haskell中最惯用的方式 您可以使用折叠将某物砍在一起,但显然不如第一个变体可读:

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

感谢@WillNess提供了上述功能的更具可读性的版本,尽管它在简洁性和清晰度方面仍然没有击败zipWith

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

这真是骇人听闻的。 在这种情况下使用折叠的问题在于,通常将单独处理元素,并且它们不会“知道”其邻居。 他们知道的唯一值是累加器和它们自己的值。

在这个例子中,做f (<) [1,2,3,4,5]将检查是否有对左侧元件是比前一个小,但它比只是做方式以下可读:

f p xs = zipWith p xs (tail xs)

用法与上面相同。


请注意,我不是Haskell语言的专业人士,因此可能使用一种更好的方式使用折痕编写了这种方法,但是它永远都不会击败zipWith方法的优雅之zipWith

您可以在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

然后,您可以专门使用此函数来处理非空列表(因为您需要知道至少一个元素来提供第一个种子值),如下所示:

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

通过这种方式拆分代码,您还可以在其他结构上定义zipSelf ,例如TreeMaybe ,或者可以轻松提取第一个元素的任何类型。

谢里奥

你可以写一个像

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

(随意选择一个更好的名称,这个名称很糟糕),但是在扩展时,它在计算上与zipWith fx (drop 1 x)相同。 请注意,我使用drop 1而不是tail ,这是因为tail是部分函数,​​而tail []导致运行时错误,而drop 1 []返回[] 由于这几乎与zipWith变体相同,因此我更喜欢zipWith因为它是单行的,而且我不必手动进行递归。

您可以使用mapAccumL

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

这将产生输出[True,True,True,True,True]。 如果希望它忽略第一个列表项,则可以创建如下函数:

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