Greaters function define

情到浓时终转凉″ 提交于 2019-12-30 23:21:48

问题


I would like to define a greaters function, which selects from a list items that are larger than the one before it.

For instance:

greaters [1,3,2,4,3,4,5] == [3,4,4,5]
greaters [5,10,6,11,7,12] == [10,11,12]

The definition I came up with is this :

greaters :: Ord a => [a] -> [a]

Things I tried so far:

greaters (x:xs) = group [ d | d <- xs, x < xs ]

Any tips?


回答1:


I would start from here:

greaters :: Ord a => [a] -> [a]
greaters [] = []
greaters (x:xs) = greatersImpl x xs
    where
        greatersImpl last [] = <fill this out>
        greatersImpl last (x:xs) = <fill this out>



回答2:


We can derive a foldr-based solution by a series of re-writes starting from the hand-rolled recursive solution in the accepted answer:

greaters :: Ord a => [a] -> [a]
greaters [] = []
greaters (x:xs) = go x xs          -- let's re-write this clause
  where
    go _ [] = []
    go last (act:xs)
      | last < act  =  act : go act xs
      | otherwise   =        go act xs

greaters (x:xs) = go xs x          -- swap the arguments
  where
    go [] _ = []
    go (act:xs) last
      | last < act  =  act : go xs act 
      | otherwise   =        go xs act 

greaters (x:xs) = foldr g z xs x   -- go ==> foldr g z
  where
    foldr g z [] _ = []
    foldr g z (act:xs) last
      | last < act  =  act : foldr g z xs act 
      | otherwise   =        foldr g z xs act 

greaters (x:xs) = foldr g z xs x
  where                          -- simplify according to
    z _ = []                     --   foldr's definition
    g act (foldr g z xs) last 
      | last < act  =  act : foldr g z xs act 
      | otherwise   =        foldr g z xs act 

Thus, with one last re-write of foldr g z xs ==> r,

greaters (x:xs) = foldr g z xs x
  where
    z = const []
    g act r last
      | last < act  =  act : r act 
      | otherwise   =        r act 

The extra parameter serves as a state being passed forward as we go along the input list, the state being the previous element; thus avoiding the construction by zip of the shifted-pairs list serving the same purpose.




回答3:


The following functions are everything you’d need for one possible solution :)

zip :: [a] -> [b] -> [(a, b)]
drop 1 :: [a] -> [a]
filter :: (a -> Bool) -> [a] -> [a]
(<) :: Ord a => a -> a -> Bool
uncurry :: (a -> b -> c) -> (a, b) -> c
map :: (a -> b) -> [a] -> [b]
snd :: (a, b) -> b

Note: drop 1 can be used when you’d prefer a “safe” version of tail.




回答4:


If you like over-generalization like me, you can use the witherable package.

{-# language ScopedTypeVariables #-}

import Control.Monad.State.Lazy
import Data.Witherable

{-
class (Traversable t, Filterable t) => Witherable t where
  -- `wither` is an effectful version of mapMaybe. 
  wither :: Applicative f => (a -> f (Maybe b)) -> t a -> f (t b)
-}

greaters
  :: forall t a. (Ord a, Witherable t)
  => t a -> t a
greaters xs = evalState (wither go xs) Nothing
  where
    go :: a -> State (Maybe a) (Maybe a)
    go curr = do
      st <- get
      put (Just curr)
      pure $ case st of
        Nothing -> Nothing
        Just prev ->
          if curr > prev
            then Just curr
            else Nothing

The state is the previous element, if there is one. Everything is about as lazy as it can be. In particular:

  1. If the container is a Haskell list, then it can be an infinite one and everything will still work. The beginning of the list can be produced without withering the rest.

  2. If the container extends infinitely to the left (e.g., an infinite snoc list), then everything will still work. How can that be? We only need to know what was in the previous element to work out the state for the current element.




回答5:


"Roll your own recursive function" is certainly an option here, but it can also be accomplished with a fold. filter can't do it because we need some sort of state being passed, but fold can nicely accumulate the result while keeping that state at the same time.

Of course the key idea is that we keep track of last element add the next one to the result set if it's greater than the last one.

greaters :: [Int] -> [Int]
greaters [] = []
greaters (h:t) = reverse . snd $ foldl (\(a, r) x -> (x, if x > a then x:r else r)) (h, []) t

I'd really love to eta-reduce it but since we're dropping the first element and seeding the accumulator with it it kinda becomes awkward with the empty list; still, this is effectively an one-liner.




回答6:


So i have come up with a foldr solution. It should be similar to what @Will Ness has demonstrated but not quite i suppose as we don't need a separate empty list check in this one.

The thing is, while folding we need to encapsulate the previous element and also the state (the result) in a function type. So in the go helper function f is the state (the result) c is the current element of interest and p is the previous one (next since we are folding right). While folding from right to left we are nesting up these functions only to run it by applyying the head of the input list to it.

go :: Ord a => a -> (a -> [a]) -> (a -> [a])
go c f = \p -> let r = f c 
               in if c > p then c:r else r

greaters :: Ord a => [a] -> [a]
greaters = foldr go (const []) <*> head

*Main> greaters [1,3,2,4,3,4,5]
[3,4,4,5]
*Main> greaters [5,10,6,11,7,12]
[10,11,12]
*Main> greaters [651,151,1651,21,651,1231,4,1,16,135,87]
[1651,651,1231,16,135]
*Main> greaters [1]
[]
*Main> greaters []
[]

As per rightful comments of @Will Ness here is a modified slightly more general code which hopefully doesn't break suddenly when the comparison changes. Note that const [] :: b -> [a] is the initial function and [] is the terminator applied to the result of foldr. We don't need Maybe since [] can easily do the job of Nothing here.

gs :: Ord a => [a] -> [a]
gs xs = foldr go (const []) xs $ []
        where
        go :: Ord a => a -> ([a] -> [a]) -> ([a] -> [a])
        go c f = \ps -> let r = f [c]
                        in case ps of
                           []  -> r
                           [p] -> if c > p then c:r else r


来源:https://stackoverflow.com/questions/59068318/greaters-function-define

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!