Apply a function to a list and pass its result to a constructor?

265 Views Asked by At

How can just write flipvone time applying it to each element of list [se, sq, nw, ne], giving the result (not as a list of course) to the Q constructor?

data (Eq a, Show a) => QT a = C a | Q (QT a) (QT a) (QT a) (QT a)
    deriving (Eq, Show)

flipv :: (Eq a, Show a) => QT a -> QT a
flipv (C a) = C a
flipv (Q nw ne se sw) = Q (flipv se) (flipv sw) (flipv nw) (flipv ne)

EDIT: note this actually wrong because the pointers should be: NW NE SW SE.

4

There are 4 best solutions below

1
On BEST ANSWER

There is no particularly simple or compact method, but you could try this:

flipv :: (Eq a, Show a) => QT a -> QT a
flipv (C a) = C a
flipv (Q nw ne se sw) = Q se' sw' nw' ne'
  where [nw', ne', se', sw'] = map flipv [nw, ne, se, sw]
1
On

applying it to each element of list [se, sq, nw, ne], giving the result (not as a list of course) to the Q constructor?

takes a list and throws a QT.

data (Eq a, Show a) => QT a = C a | Q (QT a) (QT a) (QT a) (QT a)  
     deriving (Eq, Show)

flipv :: (Eq a, Show a) => [a] -> QT a  
flipv [nw, ne, se, sw] = Q (C se) (C sw) (C nw) (C ne)

main = do  
    print (Q (C 1) (C 2) (C 3) (C 4))  
    (print . flipv) [1, 2, 3, 4]
4
On

At first glance, I was going to suggest toList and fromList. It's more code, but it enables elegant composition in the end.

toList :: QT a -> [QT a]
toList (Q w x y z) = [w,x,y,z]

fromList :: [QT a] -> QT a
fromList [w,x,y,z] = Q w x y z

listOpOnQT :: ([QT a] -> [QT a]) -> QT a -> QT a
listOpOnQT _ (C a) = C a
listOpOnQT f q     = fromList . map (listOpOnQT f) . f . toList $ q

flipv :: QT a -> QT a
flipv = listOpOnQT reverse

Loosely tested in ghci

ghci> let q = Q (Q (C 1) (C 2) (C 3) (C 4)) (C 22) (C 33) (C 44)
ghci> q
Q (Q (C 1) (C 2) (C 3) (C 4)) (C 22) (C 33) (C 44)
ghci> flipv q
Q (C 44) (C 33) (C 22) (Q (C 4) (C 3) (C 2) (C 1))

You can easily make 'sort' work on your QT structure as well, now.

import Data.List (sort)
instance (Ord a) => Ord (QT a) where
    compare (C x) (C y) = x `compare` y
    compare (C x) _ = LT
    compare _ (C x) = GT
    compare _ _ = EQ

sortv :: (Ord a) => QT a -> QT a
sortv = listOpOnQT sort

Tested as part of the previous ghci session...

ghci> sortv it
Q (C 22) (C 33) (C 44) (Q (C 1) (C 2) (C 3) (C 4))
ghci> sortv q
Q (C 22) (C 33) (C 44) (Q (C 1) (C 2) (C 3) (C 4))

Notice sorting the flipped q and just plain q both came out with the same result (therefore the sorting probably works! yay). You might want to pick a better implementation of compare, I just threw that one together to see stuff happen.


So how does it work?

The magic sauce, as you might have guessed, is listOpOnQT. In the non-trivial case, it turns the QT structure into a list, applies the listy function to the list, maps the lifted listy function on all elements of the list, and then pulls the list back into a QT structure. A better name for listOpOnQT might be liftQT, though it only works for a very special kind of function...

0
On

There's basically no simple way to do it without enumerating all four arguments, because otherwise, how can you make sure that the list has the right number (4) of elements?