简体   繁体   中英

Haskell: f :: hex String -> Integer using tail recursion

I'm looking for a way to change hex to an integer using tail recursion. So far I've only tried terrible implementations of regular primitive recursion and I haven't even gotten close. Very frustrated. Even examples of tail recursion will help and be greatly appreciated. I don't quite understand it well enough for this implementation.

Example:

  • "005" -> 5
  • "1E" -> 30

Restrictions: Cannot use imports or if, then, else etc, must be done with recursion if possible or tail recursion.

My attempt at recursion.

    hexToInteger :: String -> Integer
        |(x:xs) = []        = []
        |x == 0             = hexToInteger xs
        |otherwise          = addition x + hexToInteger xs

    addition :: String -> Integer
    addition x 
        |--something to check what position we're dealing with and what hex value.
        |--Return the Integer value

Generally, for tail-recursive functions, you need an accumulator argument -- with purity, the result could otherwise only depend on the base case reached. So you would need a helper function taking also an accumulator argument, and call that with an initial value for the accumulator,

hexToInteger :: String -> Integer
hexToInteger string = hexToIntegerHelper initialAccumulator string

and you must find out

  • what initial value you should pass for the accumulator
  • how the accumulator has to be updated in each step.

For example, a tail-recursive implementation of reverse is

reverse :: [a] -> [a]
reverse xs = reverseHelper [] xs

reverseHelper :: [a] -> [a] -> [a]
reverseHelper accumulator [] = accumulator
reverseHelper accumulator (x:xs) = reverseHelper (x:accumulator) xs

and a tail-recursive factorial (fudging the case of a negative argument)

factorial :: Integer -> Integer
factorial n = factorialHelper 1 n

factorialHelper :: Integer -> Integer -> Integer
factorialHelper accumulator n
    | n < 2     = accumulator
    | otherwise = factorialHelper (n*accumulator) (n-1)

So you can see the general structure of hexToIntegerHelper ,

hexToIntegerHelper :: Integer -> String -> Integer
hexToIntegerHelper accumulator "" = accumulator
hexToIntegerHelper accumulator (d:ds) = hexToIntegerHelper (newAccumulatorFrom accumulator d) ds

and the question is how the new accumulator is to be computed from the old one and the hexadecimal digit (and what the initial accumulator should be).

For the updating of the accumulator,

digitToInt :: Char -> Int

from Data.Char could be useful, that handles all hexadecimal digits. But, it doesn't return the desired type, so you'd need to use a fromIntegral or a toInteger to convert the Int to Integer .

Here are two recursive functions, although it has been pointed out to me that they are not tail recursive. Maybe they can help you get there, though.

hexToInteger :: String -> Integer
hexToInteger [] = 0
hexToInteger str = 
  fromIntegral z + 16 * hexToInteger (init str)
    where z = let y = last str 
              in if y >= 'A' && y <= 'Z' 
                    then fromEnum y - 55 
                    else if y >= 'a' && y <= 'z'
                            then fromEnum y - 87
                            else fromEnum y - 48



hexToInteger :: String -> Integer
hexToInteger [] = 0
hexToInteger str = 
  z + 16 * hexToInteger (init str)
    where z = case last str of 
                '0' -> 0 
                '1' -> 1 
                '2' -> 2 
                '3' -> 3 
                '4' -> 4 
                '5' -> 5 
                '6' -> 6 
                '7' -> 7 
                '8' -> 8 
                '9' -> 9 
                'A' -> 10 
                'B' -> 11 
                'C' -> 12 
                'D' -> 13 
                'E' -> 14 
                'F' -> 15
                'a' -> 10 
                'b' -> 11 
                'c' -> 12 
                'd' -> 13 
                'e' -> 14 
                'f' -> 15
                otherwise -> 0

We can implement this the same way you would convert a hexadecimal string on paper: Initialize your output to 0, then read the string from left to right, each time multiplying your result by 16 and adding the new digit.

Create and call a private function with an accumulator argument initialized to 0:

hex2int s = hex2int' s 0
    where

The base case of the recursion takes an empty string and returns the accumulator:

        hex2int' "" n = n

The recursive case takes the first character of the string and increases the accumulator accordingly.

        hex2int' (c:s) n = hex2int' s ((n * 16) + (hexDigit2int c))

hexDigit2int is a simple lookup.

        hexDigit2int c
          | c == "0" = 0

         ...

          | c == "f" = 15

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