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

Haskell 在列表中的元素链对上映射函数,haskell,functional-programming,Haskell,Functional Programming,我想将一个函数映射到列表中元素的链式对上。考虑列表: a = [1,2,3,4,5] 通过使用函数f a1 a2=a1(a,p a prev:res))(last xs,[])(init xs来保持运行时间的线性,但它确实不是很优雅(它对无限列表不起作用:-)@yatima2975这就是我的意思^^fp=foldr g[]。tails,其中g(x:y:u)r=pxy:r;g u=[]。这是一个伪装的准形像。顺便说一句带pxs(tail xs)=(带p tail的zipwhith)xs。我会写f

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

a = [1,2,3,4,5]
通过使用函数
f a1 a2=a1
,我想得到:

[True,True,True,True]
以下几点似乎在总体上起作用:

zipWith f a (tail a)

不过,我觉得这有点老土。有没有更合适的方法,比如使用fold?

您可以编写如下函数

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

(请随意选择一个更好的名称,这个名称很糟糕),但当您将其展开时,它在计算上与使用f x(drop 1 x)的zipWith
相同。请注意,我使用了
drop 1
而不是
tail
,这是因为
tail
是一个部分函数,
tail[]
导致运行时错误,而
drop 1[]
返回
[]
。由于这与zipWith变体几乎相同,我更喜欢zipWith,因为它是一个单行程序,我不需要手动执行递归

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

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

我想我可以代表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(我想我可以代表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(可以在状态函子上使用可遍历实例来实现预期结果,如下所示:

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
,例如
Tree
s或
Maybe
s,或第一个元素容易提取的任何类型


Cheerio,

您可以在状态函子上使用可遍历实例来实现预期结果,如下所示:

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
,例如
Tree
s或
Maybe
s,或第一个元素容易提取的任何类型


干杯,

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

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

那么,你会如何使用折叠呢?使用
zipWith f a(tail a)
有什么不妥之处?我会说这是惯用的Haskell。
map f$zip xs(tail xs)
会按照你的标题所说的做。或者
[f x y|(x,y)那么,你会如何使用折叠呢?使用
zipWith f a(tail a)有什么不妥之处
?我想说这是惯用的Haskell。
映射f$zip xs(tail xs)
会按照你的标题所说的做。或者
[f x y|(x,y)实际上,在
中使用
tail
是很好的
,即使原因有点违反直觉。
zipWith
的第一种定义类似于
zipWith f[].=[]
,因此当
xs=[]时,
zipWith f xs(tail xs)
不会计算
tail xs
@chi很好,但我喜欢使用
drop 1
而不是tail where,因为我偏爱。listToMaybe和head也是如此。事实上,在
zipWith f xs(tail xs)中使用
tail
很好
,即使原因有点违反直觉。
zipWith
的第一种定义类似于
zipWith f[].=[]
,因此当
xs=[]时,
zipWith f xs(tail xs)
不会计算
tail xs
@chi很好,但我喜欢使用
drop 1
而不是tail where,因为我偏爱它。listToMaybe和head也是如此。我会写
f p xs=snd$foldr(\a(prev,res)->(a,p a prev:res))(last xs,[])(init xs
来保持运行时间的线性,但它确实不是很优雅(它对无限列表不起作用:-)@yatima2975这就是我的意思^^
fp=foldr g[]。tails,其中g(x:y:u)r=pxy:r;g u=[]
。这是一个伪装的准形像。顺便说一句
带pxs(tail xs)=(带p tail的zipwhith)xs
。我会写
fpxs=snd$f