有没有办法找出使用foldr
或foldl
列表的本地最大值,或者我必须使用unfoldr
,因为列表的第一个和最后一个元素不是本地的最大值?
我理解如何使用带有警卫的递归来找到它,例如
localMax :: [Int] -> [Int]
localMax [] = []
localMax (x:[]) = []
localMax (x:y:[]) = []
localMax (x:y:z:zs)
| y > z && y > x = y:localMax (y:z:zs)
| otherwise = localMax (y:z:zs)
答案 0 :(得分:8)
你可以,但它非常难看。主要是因为要找到局部最大值,您需要知道上一个和下一个元素。
前面的元素没有问题,但是折叠不应该知道它后面的元素。
作为一种可能的方法
import Data.List (zip3)
localMax xs = map proj2
. filter isLocalMax
$ zip3 xs (tail xs) (drop 2 xs)
where isLocalMax (x, y, z) = x < y && z < y
proj2 (_,x,_) = x
所以我们将列表移动一到两个并将它们全部压缩,以便[a, b, c, d, e]
成为。[(a, b, c), (b, c, d), (c, d, e)]
map
然后我们只使用filter
和map
来选择合适的元素。
请注意,filter
和foldr
可能会被打成一个{{1}},如果你真的想要的话。
答案 1 :(得分:3)
localMaxAsUnfold :: [Int] -> [Int]
localMaxAsUnfold = unfoldr work
where
work (x:y:z:rest)
| y > x && y > z = Just (y, y:z:rest)
| otherwise = work (y:z:rest) -- cheat and recurse internally
work _ = Nothing
localMaxAsFold :: [Int] -> [Int]
localMaxAsFold = foldr work [] . makeTriples
where
makeTriples :: [Int] -> [(Int, Int, Int)]
makeTriples vals = zip3 vals (tail vals) (drop 2 vals)
work (x,y,z) vals
| y > x && y > z = y : vals
| otherwise = vals
答案 2 :(得分:3)
我们总是可以通过短视的 1 foldr
over (init . tails)
来模仿富有洞察力的paramorphism:
import Data.List
import Control.Applicative
localMaxima :: Ord a => [a] -> [a]
localMaxima = foldr g [] . init . tails . (zip <*> tail)
where
g ((a,b):(_,c):_) r | a<b && b>c = b:r
g _ r = r
在这种特殊情况下,init
可以省略。 zip <*> tail
(这只是\xs -> zip xs (tail xs)
的简写)形成了输入列表中连续元素对的列表。
不,我不认为它特别“丑陋”。 2 :)
1 2 cf. this answer here
2 也another answer here
答案 3 :(得分:2)
我会尝试只使用折叠,就像你问的那样。这样的事情怎么样?
lmax (x:y:xs) = third $ foldl f (x,y,[]) xs
where
third (_, _, x) = x
f (x, y, ls) z = (y, z, if y > x && y > z then y:ls else ls)
这个想法是你在折叠中传递一个元组而不是结果列表。元组(三元组)将包含最后两个元素和结果列表。该函数评估三元组的第二个元素是否是局部最小值w.r.t.第一个元素(它的前身)和由fold(它的后继者)传递的当前元素。
ghci> lmax [1,3,2]
[3]
ghci> lmax [3,4,2,5,1,7,6,1,9]
[7,5,4]
ghci> lmax [1..10]
[]
ghci> lmax []
*** Exception: test.hs:(4,1)-(5,66): Non-exhaustive patterns in function lmax
无论如何,当输入列表太短时,应该很容易使用您喜欢的任何方法来返回空结果列表。
请注意,使用foldl
时,每个新结果都会附加在顶部。因此,结果列表相反。如果您希望将这些结果与原始列表中的顺序相同,则可能需要再次撤消lmax
的结果:lmax' = reverse . lmax
。
答案 4 :(得分:2)
理念与Will Ness建议的非常接近,但没有应用拉链和更短的时间:
lm a = foldr (\(x:y:z:_) a -> if y > z && y > x then y:a else a) []
$ filter ((2<) . length) (tails a)
可读性仍然值得怀疑:)
更新根据Will Ness的建议,也可以使用列表理解:
lm a = [y | (x:y:z:_) <- tails a, x < y && y > z]
答案 5 :(得分:1)
这是另一个在列表上没有模式匹配并且仅使用fold:
g :: a -> (a -> Bool) -> (a -> Bool) -> [a]
g x p n | p x && n x = [x]
| otherwise = []
localMax :: Ord a => [a] -> [a]
localMax l = fr $ const False
where (_, fr) = foldr worker (const False, \_ -> []) l
worker x (p,f) = ((> x), \n -> g x p n ++ f (> x))
基本思想是在累加器中使用一个函数,这样我们就可以将后面的元素“传回”到前一个阶段。所以我们传回一个类型为a -> Bool
的函数,只有当下一个元素大于参数时才返回True。 (此函数在上面的代码中称为n
next
。我们在累加器中还有另一个类型a -> Bool
的函数,如果前一个元素小于传递的值(p
称为previous
),则返回True。当两个函数都返回True时,我们有一个最大值。这是g
检查的内容。
*Main> localMax [3,4,2,5,1,7,6,1,9]
[4,5,7]
*Main> localMax [1..10]
[]
*Main> localMax []
[]
答案 6 :(得分:1)
这是另一个带有zipWith的文件
localMax a = [ x | (x,v) <- zip a localMaxInd, v]
where
localMaxInd = zipWith (&&) u v
where
u = False : zipWith (>) (tail a) a
v = zipWith (>) a (tail a)
测试用例
> localMax [3,4,2,5,1,7,6,1,9]
[4,5,7]
答案 7 :(得分:0)
我想@ jozefg的答案, 可以用折叠来表达整个事情。列表上的任何递归操作最终都可以使用foldr
表示,但通常很难看。
首先让我们使用mapMaybe
和zip
来实现locMax
,与@jozefg所做的非常相似::
import Prelude hiding (zip)
isMax :: (Ord a) => ((a, a), a) -> Maybe a
isMax ((x, y), z) | y > z && y > x = Just y
| otherwise = Nothing
locMax :: (Ord a) => [a] -> [a]
locMax xs@(_:xs'@(_:xs'')) = mapMaybe isMax $ zip (zip xs xs') xs''
使用mapMaybe
实施foldr
并不困难:
mapMaybe :: (a -> Maybe b) -> [a] -> [b]
mapMaybe f = foldr (\x xs -> maybe xs (: xs) (f x)) []
实现zip
有点棘手,因为我们需要一次使用两个列表。我们要做的是,我们将在foldr
内积累[b] -> [(a,b)]
类型的函数,该函数将使用第二个列表。
基本情况很简单。如果第一个列表为空,则构造的函数为const []
,因此无论第二个列表是什么,结果也都是空的。
折叠步骤采用值x : a
,这是一个转换[b]
子列表的累积函数。既然我们再次生成一个函数,我们只需要第三个[b]
类型的参数。如果没有b
s,则结果为空列表。如果至少有一个,我们构建一对并在其余f
s上调用b
:
zip :: [a] -> [b] -> [(a,b)]
zip = foldr step (const [])
where
step _ _ [] = []
step x f (y : ys) = (x, y) : f ys
您可以验证此实现是否具有所需的属性,并且如果一个或两个列表是无限的,也可以正常工作。