3
data RoseTree a = RoseNode a [RoseTree a] deriving Show

things :: RoseTree String
things = 
    RoseNode "thing" [
        RoseNode "animal" [
            RoseNode "cat" [], RoseNode "dog" []
        ],

        RoseNode "metal" [
            RoseNode "alloy" [
                RoseNode "steel" [], RoseNode "bronze" []
            ],
            RoseNode "element" [
                RoseNode "gold" [], RoseNode "tin" [], RoseNode "iron" []
            ]
        ],       
    ] 
-- Turns string into all upper case
allCaps :: String -> String
allCaps x = map toUpper x
-- This function uses allCaps as a helper function 
--  to turn the elements in tree into upper case
roseMap :: (a -> b) -> RoseTree a -> RoseTree b
roseMap  f  rtree = case rtree of
    RoseNode a []    ->  allCaps a
    RoseNode a sub   ->  Rose (allCaps a) (map (roseMap f sub)

that takes a function, and applies it to every element of a rosetree. Test the result by mapping the function allCaps to the rosetree things. All the elements should now be written in uppercase.

I have no idea about how to use recursion to write this function.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
Jayyyyyy
  • 197
  • 1
  • 10
  • 2
    Hint: What should happen to the value at the top of the rose tree, and what should happen to each of the sub-trees? – bradrn Oct 07 '19 at 10:53
  • It should change the element in the top of the rose tree at the first, and then apply the same function to the rest of elements but I don't know how to translate this into function. – Jayyyyyy Oct 07 '19 at 10:59

1 Answers1

4

You are already using recursion. Indeed, you use roseMap f in terms of itself:

roseMap :: (a -> b) -> RoseTree a -> RoseTree b
roseMap f  rtree = case rtree of
    RoseNode a [] -> allCaps a
    RoseNode a sub -> Rose (allCaps a) (map (roseMap f sub))

But the above will not work for several reasons:

  1. you do not make use of f here. Indeed you write Rose (allCaps a), so you do not use f to map the elements;
  2. the sub should not be passed in the brackets of roseMap f;
  3. you should use RoseNode instead of Rose; and
  4. your first case, you return allCaps a, instead of RoseNode (allCaps a) [].

Making a distinction between a node with no children, or with children, is not necessary anway. We can define the mapping as:

roseMap :: (a -> b) -> RoseTree a -> RoseTree b
roseMap f (RoseNode a xs) = RoseNode (f a) (map (roseMap f) xs)

So here we use f a instead, and we perform a mapping on the children.

If we then perform a roseMap with allCaps as function, we get:

Prelude Data.Char> roseMap allCaps things
RoseNode "THING" [RoseNode "ANIMAL" [RoseNode "CAT" [],RoseNode "DOG" []],RoseNode "METAL" [RoseNode "ALLOY" [RoseNode "STEEL" [],RoseNode "BRONZE" []],RoseNode "ELEMENT" [RoseNode "GOLD" [],RoseNode "TIN" [],RoseNode "IRON" []]],RoseNode "FRUIT" [RoseNode "APPLE" [RoseNode "GRANNY SMITH" [],RoseNode "PINK LADY" []],RoseNode "BANANA" [],RoseNode "ORANGE" []],RoseNode "ASTRONOMICAL OBJECT" [RoseNode "PLANET" [RoseNode "EARTH" [],RoseNode "MARS" []],RoseNode "STAR" [RoseNode "THE SUN" [],RoseNode "SIRIUS" []],RoseNode "GALAXY" [RoseNode "MILKY WAY" []]]]

We do not need to implement the mapping ourself, we can enable the DeriveFunctor extension [ghc-doc], and let Haskell do the work for us:

{-# LANGUAGE DeriveFunctor #-}

data RoseTree a = RoseNode a [RoseTree a] deriving (Functor, Show)

the we can call this with fmap :: Functor f => (a -> b) -> f a -> f b:

Prelude Data.Char> fmap (map toUpper) things
RoseNode "THING" [RoseNode "ANIMAL" [RoseNode "CAT" [],RoseNode "DOG" []],RoseNode "METAL" [RoseNode "ALLOY" [RoseNode "STEEL" [],RoseNode "BRONZE" []],RoseNode "ELEMENT" [RoseNode "GOLD" [],RoseNode "TIN" [],RoseNode "IRON" []]],RoseNode "FRUIT" [RoseNode "APPLE" [RoseNode "GRANNY SMITH" [],RoseNode "PINK LADY" []],RoseNode "BANANA" [],RoseNode "ORANGE" []],RoseNode "ASTRONOMICAL OBJECT" [RoseNode "PLANET" [RoseNode "EARTH" [],RoseNode "MARS" []],RoseNode "STAR" [RoseNode "THE SUN" [],RoseNode "SIRIUS" []],RoseNode "GALAXY" [RoseNode "MILKY WAY" []]]]
Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • Thank you for you answer. I still have a question. The recursion of haskell is difficult to imagine, I mean I cannot imagine the implementation process of the recursion. So writing a correct recursion expression is hard. Can I expand the execution of function on paper, or something like that to make me better understand recursion. – Jayyyyyy Oct 07 '19 at 11:25
  • 2
    Recursion in Haskell works no differently from recursion in any other language. It's just a lot more common in Haskell (and functional languages in general), both because it's often the only way to accomplish things that you'd use loops for in an imperative language, and because it's much more efficient in Haskell than in imperative languages. (I couldn't give you a detailed explanation as to why, but laziness is a big part of it.) – Robin Zigmond Oct 07 '19 at 13:21
  • 1
    As for how the recursion works in this specific case: a rosetree consists of a label of type `a`, and a list of other (sub)trees. Applying `roseMap f` to such a tree simply replaces the `a` with `f a`, and each subtree `sub` with `roseMap f sub`. The recursion eventually ends (at least in practice, it doesn't have to in theory) when the list of subtrees is empty. If you try working through this with your example tree, or simpler ones, I'm confident you'll soon understand how it works. – Robin Zigmond Oct 07 '19 at 13:25