在列表中的链接元素对上映射函数

时间:2014-09-10 16:47:15

标签: haskell functional-programming

我想在列表中的链式元素对上映射函数。考虑清单:

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

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

[True,True,True,True]

以下似乎总体上起作用:

zipWith f a (tail a)

然而,我发现它有点像黑客。有没有更合适的方式,也许使用折叠?

4 个答案:

答案 0 :(得分:6)

如果我告诉你在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 _ _ = []

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

在此示例中,执行f (<) [1,2,3,4,5]将检查对中的左侧元素是否小于之前的元素,但方式的可读性低于仅执行此操作:

f p xs = zipWith p xs (tail xs)

用法与上述相同。


请注意,我不是Haskell语言的专业人士,所以可能这种使用折叠的方法可能是以更好的方式编写的,但是它永远不会超越zipWith方法的优雅。

答案 1 :(得分:3)

您可以在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 s,或者第一个元素易于提取的任何类型。

Cheerio,

答案 2 :(得分:2)

你可以写一个像

这样的函数
movingApply :: (a -> a -> b) -> [a] -> [b]
movingApply f (x:y:xs) = f x y : movingApply f (y:xs)
movingApply f _ = []

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

答案 3 :(得分:1)

您可以使用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' [] = []