Adding two functions together in Haskell

后端 未结 3 1626
刺人心
刺人心 2021-01-17 02:04

Hi I am new in Haskell and I came across an interesting problem but I was not really sure on how I would go about solving it. I am about to show you only two parts of the qu

相关标签:
3条回答
  • 2021-01-17 02:09

    The missing component is a way to break down an integer into its digits, and build it back up from there. That's easy:

    digits:: Int -> [Int]
    digits = map (`mod` 10) . takeWhile (/= 0) . iterate (`div` 10)
    
    undigits :: [Int] -> Int
    undigits = foldr f 0 where f i r = 10 * r + i
    

    Then it looks like you need to post-process those digits in two different ways, but only if they match a predicate. Let's build a combinator for that:

    when :: (a -> Bool) -> (a -> a) -> a -> a
    when p f a = if p a then f a else a
    

    The first case appears when you want to double digits in odd position (from left to right). Again trivial, with the minor inconvenience that digits breaks down a number by increasing power of ten. Let's prefix each number by its position:

    prefix :: [Int] -> [(Int, Int)]
    prefix is = let n = length is in zip [n, n-1..1] is
    

    doubleOdd can now be expressed as

    doubleodd :: [Int] -> [Int]
    doubleodd = map (snd . when (odd . fst) (id *** double)) . prefix
    

    You mentioned in a comment that when the double number overflows, its digits must be added together. This is the second case I was referring to and is again simplicity itself:

    double :: Int -> Int
    double = when (>= 10) (sum . digits) . (* 2)
    

    Here is your final program:

    program = undigits . doubleodd . tail . digits
    

    ... assuming the "between 13 and 15 digits" part is verified separately.

    0 讨论(0)
  • 2021-01-17 02:10

    I hope this helps and realize it could be cleaned up a lot. List indices start with 0 which is also an even number and the first element of a list. The list comprehension processes 0,2,4 ... the 1st,2nd and 3rd items.

    let f n = [mod n 10] ++ f (div n 10)
    let r = [if even i then d*2 else d|(i,d)<-zip [0..] (init.reverse.take 14.f$19283828382133)]
    sum [b*(10^a)|(a,b) <- zip [12,11..0] r]
    

    2948684868416

    If you want it to handle any length number, the easiest way here is length $ show 19283828382133 but I do have a function somewhere that does that. Use the length as a value in 3 places, once at full value in thetake function in the composition.

    0 讨论(0)
  • 2021-01-17 02:16

    First, you need a way to break some large number into digits.

    digits :: Integral x => x -> [x]
    digits 0 = []
    digits x = digits (x `div` 10) ++ [x `mod` 10]
    

    Which gives you...

    Prelude> digits 12345
    [1,2,3,4,5]
    

    You can then drop the last digit with init

    Prelude> (init . digits) 12345
    [1,2,3,4]
    

    The a helper function to map over odd elements in a list.

    mapOdd _ [] = []
    mapOdd f (x:[]) = [f x]
    mapOdd f (x:y:rest) = f x : y : mapOdd f rest
    

    Giving you...

    Prelude> mapOdd (+10) [1..10]
    [11,2,13,4,15,6,17,8,19,10]
    

    And a function to get back to a large number...

    undigits = sum . zipWith (*) [10^n | n <- [0..]] . reverse
    

    Resulting in...

    Prelude> undigits [1, 2, 3, 4]
    1234
    

    And putting it all together

    Prelude> undigits . mapOdd (*2) . init . digits $ 12345
    2264
    

    In functional languages particularly, always try to solve a problem by composing solutions to smaller problems :)

    0 讨论(0)
提交回复
热议问题