1

Tried to mimic the reverse function to the ADT List below,

 data List a = Nil | Cons a (List a) deriving (Show)                                                                   

 reverseList = convert . reverse'                                                                                      
     where                                                                                                             
         reverse' Nil = []                                                                                             
         reverse' (Cons x list) = reverse' list ++ [x]                                                                 

         convert [] = Nil                                                                                              
         convert (x:xs) = Cons x (convert xs)  

The reverseList is composed of convert and reverse', how to achieve it optimally for instance without the aid of the built-in list?

sof
  • 9,113
  • 16
  • 57
  • 83

4 Answers4

2

Starting from this answer to a similar question, you can let the compiler derive a Traversable instance to your List type:

{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE DeriveFoldable #-}
{-# LANGUAGE DeriveTraversable #-}

data List a = Nil | Cons a (List a) deriving (Show, Functor, Foldable, Traversable)
--You need the Functor and Foldable instances

Then, reverse' becomes simply:

reverse' = foldl (flip Cons) Nil

which is O(N) time complexity.

MikaelF
  • 3,518
  • 4
  • 20
  • 33
2

In my view, the most basic O(N) solution is to use recursion and an accumulator:

revlist :: List a -> List a
revlist xs = go xs Nil
   where
   go ::  List a -> List a -> List a
   go Nil         zs = zs
   go (Cons y ys) zs = go ys (Cons y zs)

This exploits go, a tail-recursive function which essentially uses the two lists as stacks, repeatedly popping elements form the first stack and pushing them to the second stack. Once the first stack is empty, the second stack contains the reversed list.

chi
  • 111,837
  • 3
  • 133
  • 218
  • This is exactly how [reverse](https://hackage.haskell.org/package/base-4.12.0.0/docs/src/GHC.List.html#reverse) in `Data.List` does! – sof Dec 11 '19 at 14:47
1

All you need to do is translate all the list functions you're using to ones on your List type. Here this means ++ - let's define this for your version, and call it say listConcat (feel free to come up with your own name):

listConcat :: List a -> List a -> List a
listConcat Nil as = as
listConcat (Cons a as) bs = Cons a (listConcat as bs) 

And then you can just translate the definition of reverseList that you already have:

reverseList :: List a -> List a
reverseList Nil = Nil
reverseList (Cons a as) = listConcat (reverseList as) (Cons a Nil) 
Robin Zigmond
  • 17,805
  • 2
  • 23
  • 34
  • 3
    I think it should be noted that this has still quadratic complexity while it could be O(N), e.g. using an accumulator-based recursive function. OP asks for "optimality", and I am unsure about what they actually want. – chi Dec 10 '19 at 22:31
  • Good point @chi, I interpreted the OP as just asking to write the function pure in terms of the `List` type without involving the built-in list type, and hadn't even considered complexity or how to reduce it. But I will happily admit, having thought about it, that this is `O(n^2)` and therefore very bad. – Robin Zigmond Dec 10 '19 at 22:34
1

You may perhaps do it in an applicative style efficiently. It should be O(n)

data List a = Nil | Cons a (List a) deriving Show

conc :: List a -> List a -> List a
conc Nil xs = xs
conc (Cons x xs) ys = Cons x $ conc xs ys

revlist :: List a -> List a
revlist xs = go xs Nil                                  -- invoke go xs function by Nil
             where
             go ::  List a -> (List a -> List a)
             go Nil         = conc Nil                  -- such as ([]++)
             go (Cons x xs) = go xs . conc (Cons x Nil) -- such as go xs . ([x]++)

λ> revlist (Cons 1 (Cons 2 (Cons 3 Nil)))
Cons 3 (Cons 2 (Cons 1 Nil))

We are nesting the concatenation operations (conc) to the right. This is basically difference lists.

Redu
  • 25,060
  • 6
  • 56
  • 76
  • 2
    This is O(N), but I find it a bit more complex than needed. We can replace `conc Nil` with `id`. Further, `conc (Cons x Nil)` can be written as `Cons x`. At that point, `conc` is no longer needed. – chi Dec 11 '19 at 00:17
  • @chi I shouldn't say that i have thought your points and left them so deliberately since i haven't... You are right, however i think, still it's fine this way since along with the comments in the code, they might be helpful for those who might get curious to understand what's going on. – Redu Dec 11 '19 at 00:30