[英]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
,例如Tree
或Maybe
,或者可以轻松提取第一个元素的任何类型。
谢里奥
你可以写一个像
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.