简体   繁体   中英

Haskell - recursion start with the final term

Suppose I have the following data structure defined in my haskell code

data Exp = Expnum Int - constant
          | Expplus Exp Exp - addition

data Pair = Pnum Int
           | Plus Pair Pair  

I would like to write a function that can convert the expression to following:

-- Example: (Expplus (Expplus (Enum 5) (Enum 5)) Enum 6)
-- equivalent to ((5+5)+6)
convert :: Exp -> Pair
...

-- Expected output: Plus (Pnum 5 (Plus (Pnum 5) (Pnum 6)) 
-- equivalent to (5+(5+6))

If here the expected entry is Plus((Plus (Pnum 5) (Pnum 5)) (Pnum 6) I will have no problem writing a function like this.

However, I have no idea how can I write a recursion that starts with the final term, while when I parse in the expression it will be (Expplus (Enum 5) (Enum 5)) that is evaluated first and being returned back

First let's fix the definitions (syntax for comment wrong and I want to see the result so Pair needs to be in Show ), and your exampel:

data Exp = Expnum Int
          | Expplus Exp Exp

data Pair = Pnum Int
           | Plus Pair Pair  
           deriving (Show)

example :: Exp
example = Expplus (Expplus (Expnum 5) (Expnum 5)) (Expnum 6)

now the idea is to first flatten the Exp or if you like to extract the numbers in there:

extractConstants :: Exp -> [Int]
extractConstants (Expnum n) = [n]
extractConstants (Expplus a b) = extractConstants a ++ extractConstants b

this yields

> extractConstants example
[5,5,6]

the next step is to build from this list your final Pair :

buildPair :: [Int] -> Pair
buildPair xs = foldr1 Plus $ map Pnum xs

so first we add wrap each number in a Pnum and then just foldr1 using Plus - you can eta-reduce the xs here (see bellow).

note that this is not matching on [] - it will not matter but you should not have this on the top-level IMO - we'll fix that later

this one gives for the list the desired result:

> buildPair [5,5,6]
Plus (Pnum 5) (Plus (Pnum 5) (Pnum 6))

so all that is left is to combine those two:

convert :: Exp -> Pair
convert = buildPair . extractConstants

and that's it:

> convert example
Plus (Pnum 5) (Plus (Pnum 5) (Pnum 6))

now the issue with the missing case is left - if you think about it it will never happen for a list resulting form extractConstants (there can be no empty-list here as there is no way to express this with Exp . In this case I'd put at least this helper function as a where - here I choose to do both:

convert :: Exp -> Pair
convert = buildPair . extractConstants
  where
    extractConstants (Expnum n) = [n]
    extractConstants (Expplus a b) = extractConstants a ++ extractConstants b

    buildPair = foldr1 Plus . map Pnum

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM